Quando a requisição vira transação: mover a fronteira transacional para dentro do PostgreSQL
Alexey Evlampiev propõe separar a borda de rede da operação transacional e entregar a segunda ao banco, onde a autoridade sobre os dados já vive. Ganho central: uma autoridade, uma transação, uma prova executável.

The "just use Postgres" movement of the last five years consolidated storage: the queue became a table with FOR UPDATE SKIP LOCKED, the cache became an unlogged table, the search index became tsvector, and the vector store became pgvector. In an article published on Planet PostgreSQL, Alexey Evlampiev argues that consolidation stopped one layer too early: the data converged inside PostgreSQL, but the logic that governs it remained scattered across the application layer. That seam is what the piece works on.
The two authorities of a transactional API
Evlampiev's central thesis is that a transactional API has two halves. The first is the network edge: it authenticates the caller, terminates TLS, does rate limiting, parses the JWT and adapts HTTP. The second is the transactional boundary: it resolves which operation is being requested, validates the input, authorizes it against the current state, executes the transition and formats the result.
Convention places the first half in a gateway and the second in an application framework (ASP.NET Core, FastAPI, Spring, Express). The problem, according to the author, is that every authoritative decision in the second half already ends up in PostgreSQL. In other words: the framework keeps a second copy of the schema in classes, a second copy of the constraints in validators, a second copy of the authorization rules in middleware, and a transaction boundary that only approximately coincides with the business operation.
The proposal is to move that boundary to where the authority already lives. The unit of design stops being the REST route and becomes the transactional operation: a named database operation, with a typed contract, an authorization policy, a declared transaction, an implementation and tests. Each protocol surface (REST first, then OpenAPI, then MCP tools) becomes just a binding for that operation.
The proof that fits in seven lines
The destination of the argument is summarized by the author himself in a SQL snippet:
BEGIN;
SELECT api.invoke('POST', '/orders', ''::hstore,
'{"customerId": "…", "total": 99.95}');
-- assert on the response AND on the row it created,
-- both visible in the same open snapshot
ROLLBACK; -- the proof ran; nothing persistedThe point isn't that an API can be invoked from inside SQL. It's that the response and the state transition remain inside a single open transaction when the assertion runs: no second interface, no orchestration between observations, nothing committed yet. The gain compresses into three properties: one authority, one transaction, one executable proof. When the boundary moves, the same transaction becomes simultaneously the boundary of implementation, of authorization, and of testing.
The two taxes the traditional model pays
The strongest part of the article is its inventory of costs, backed by the frameworks' own documentation. Evlampiev separates two distinct "taxes," each with a different remedy:
The divided-authority tax (the same rule in two places, which diverge):
- Validation. Rails' documentation admits that its uniqueness validator "does not guarantee the absence of duplicate record insertions, because uniqueness checks at the application level are inherently subject to race conditions," and recommends a unique index on the database as the best solution. Django documents that
full_clean()is not automatically called onsave(). The application validator is a copy of convenience; the constraint is the law, and the copies derive from it.
The divided-execution tax (one logical operation broken across the network):
- Joins. Prisma's documentation states that its default strategy "sends multiple queries to the database (one per table) and joins them at the application level," and that this was the only supported strategy before February 2024. A mainstream ORM reimplemented the join, an operation query planners have optimized for four decades, inside the process's own memory. GitLab maintains a dedicated test framework just to keep the N+1 problem from silently coming back.
- Transactions. GitLab's handbook bans network calls inside transactions, a rule enforced through code review because no runtime can ban arbitrary I/O inside a transaction. Once the operation crosses a single ACID boundary, the transactional outbox and saga patterns become necessary. These are the right tools for genuinely distributed work, the author says, but the flaw is reaching for them when the work never needed to leave a single transaction in the first place.
The real-cost case: idle-in-transaction
The article brings a concrete number from December 2025. IBM's MCP gateway documented that, under 1,000 concurrent users, 402 database connections (65% of the pool) sat idle-in-transaction while exactly one connection was executing queries. The database work per request was ~20ms, but the sessions stayed stuck across network I/O measured in seconds to minutes.
Evlampiev's diagnosis is precise: because the transaction boundary sat in the application, application latency became the database's concurrency problem. The obvious objection is that this is a bug, not an architecture, since competent teams ban holding a session open during network I/O. And that's exactly the point: the architecture makes the bug writable, and a review rule is all that stands between it and production. A boundary where the transaction opens and commits inside the database simply cannot express this defect.
The API contract as data
At the practical layer, the author shows that every primitive of a transactional operation can be made explicit, typed, and queryable inside PostgreSQL. The HTTP message becomes a composite type:
CREATE TYPE api.http_request AS (
method api.http_method,
url text,
headers hstore,
content jsonb
);The headers are hstore because a block of HTTP headers is essentially a flat map of strings to strings, exactly the shape the extension has carried since 2006 (and, as a trusted extension installable without superuser, since PostgreSQL 13). The body, in turn, is jsonb, which succeeded hstore for nested documents. It's the same discipline as the traditional DBA: correct modeling first, types that constrain what's acceptable before any code.
Where it isn't worth it
Evlampiev is explicit about scope, and the skeptical reviewer appreciates it. The architecture serves systems whose valuable behavior consists mainly of transactional decisions over state in PostgreSQL. It is not an argument for moving network I/O, media processing, streaming, orchestration, or arbitrary compute into the database.
The author himself cites the concession made by authentik, which in 2025 removed Redis as a mandatory dependency and moved caching into PostgreSQL, reporting a simpler architecture and two to three fewer queries per request, but admitting that "PostgreSQL wasn't built for this; Redis PubSub was," with a performance drop in WebSocket relaying. Vertical scalability is the only one of the six classic objections against logic in the database that he acknowledges as structural, rather than as a tooling problem from 2011.
It's worth noting the distinction that runs through the whole piece: consolidation benefits belong to PostgreSQL and reach any layer that connects to it ("I can issue the same SQL from Go" is true); positioning benefits exist only because of where the boundary sits. It's this second category the article argues for.
What it leaves for the Brazilian developer
The argument isn't that frameworks make good discipline impossible, but that they leave it optional, guaranteed only by review and vigilance. A careful team can already issue one "fat" query instead of a chatty sequence, treat constraints as authority, and keep transactions free of network waits. Moving the transactional boundary inside the database turns these disciplines into invariants: a handler that runs inside the transaction cannot wait on the network, cannot validate against a schema different from the one it commits against, and cannot be deployed separately from the constraints it depends on.
Evlampiev's closing provocation deserves reflection from anyone who has already adopted Testcontainers (whose Docker Hub pulls doubled from 50 to 100 million in a year): if an honest test already requires the real database in the loop, what exactly is gained by keeping the logic somewhere else? The answer isn't dogmatic, but the piece is a dense work of architectural reasoning, and the source link on Planet PostgreSQL is worth the full read for anyone dealing with performance and concurrency in production.
Translated from the Brazilian Portuguese original · Read the original
Web tool inspects PostgreSQL pg_dump without restoring to a server
PostgreSQL Dump Viewer replays the backup file inside the browser to check tables, foreign keys, and run read-only SQL before any real restore.


