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.

O movimento "just use Postgres↳PostgreSQL11 conteúdosPostgreSQL via SSL com GolangData · abr 20195 itens legais sobre data types do PostgreSQLData · mar 20195 serviços gratuitos na cloud para bancos de dados PostgresData · fev 2025Ver tudo em Data →" dos últimos cinco anos consolidou storage: a fila virou tabela com FOR UPDATE SKIP LOCKED, o cache virou tabela unlogged, o índice de busca virou tsvector e o vector store virou pgvector. Em um artigo publicado no Planet PostgreSQL, Alexey Evlampiev argumenta que a consolidação parou uma camada cedo demais: os dados se juntaram no PostgreSQL, mas a lógica que os governa continuou espalhada na camada de aplicação. É sobre essa costura que o texto trabalha.
As duas autoridades de uma API transacional
A tese central de Evlampiev é que uma API transacional tem duas metades. A primeira é a borda de rede: autentica o chamador, termina TLS, faz rate limiting, parseia JWT e adapta HTTP. A segunda é a fronteira transacional: resolve qual operação está sendo pedida, valida a entrada, autoriza contra o estado atual, executa a transição e formata o resultado.
A convenção coloca a primeira metade num gateway e a segunda num framework de aplicação (ASP.NET↳.NET113 conteúdosNovidades do .NET 9 – o tipo genérico OrderedDictionaryDev (Back & Front) · dez 2024Novidades .NET 10: novas formas de uso do .NET CLIDev (Back & Front) · dez 2025Novidades do .NET 5: implementando um proxy reverso com YARP + ASP.NET 5Dev (Back & Front) · nov 2020Ver tudo em Dev (Back & Front) → Core, FastAPI, Spring, Express). O problema, segundo o autor, é que toda decisão autoritativa da segunda metade já termina no PostgreSQL. Ou seja: o framework mantém uma segunda cópia do schema em classes, uma segunda cópia das constraints em validators, uma segunda cópia das regras de autorização em middleware e uma fronteira de transação que só aproximadamente coincide com a operação de negócio.
A proposta é mover essa fronteira para onde a autoridade já mora. A unidade de projeto deixa de ser a rota REST e passa a ser a operação transacional: uma operação de banco nomeada, com contrato tipado, política de autorização, transação declarada, implementação e testes. Cada superfície de protocolo (REST primeiro, depois OpenAPI, depois ferramentas MCP↳MCP7 conteúdosArquitetura de Sistemas Cognitivos: Integração de RAG, MCP e LLMs no Ecossistema .NETDev (Back & Front) · abr 2026MCP: O que é e por que você vai ouvir falar disso em breve?AI · jul 2025Agentes de IA com LLMs de Código Aberto: Integração Prática com o Model Context Protocol (MCP)AI · ago 2025Ver tudo em AI →) vira apenas um binding para essa operação.
A prova que cabe em sete linhas
O destino do argumento é resumido pelo próprio autor em um trecho de SQL:
BEGIN;
SELECT api.invoke('POST', '/orders', ''::hstore,
'{"customerId": "…", "total": 99.95}');
-- assert na resposta E na linha que ela criou,
-- ambas visíveis no mesmo snapshot aberto
ROLLBACK; -- a prova rodou; nada persistiuO ponto não é que uma API possa ser invocada de dentro do SQL. É que a resposta e a transição de estado continuam dentro de uma única transação aberta quando a asserção roda: sem segunda interface, sem orquestração entre observações, nada ainda commitado. O ganho comprime-se a três propriedades: uma autoridade, uma transação, uma prova executável. Quando a fronteira se move, a mesma transação passa a ser simultaneamente a fronteira de implementação, de autorização e de teste.
Os dois impostos que o modelo tradicional paga
A parte mais forte do artigo é o inventário de custos, sustentado com a documentação dos próprios frameworks. Evlampiev separa dois "impostos" distintos, com remédios diferentes:
Imposto da autoridade dividida (a mesma regra em dois lugares, que divergem):
- Validação. A documentação do Rails admite que seu validador de unicidade "não garante a ausência de inserções duplicadas, porque checagens de unicidade no nível da aplicação são inerentemente sujeitas a condições de corrida" e recomenda um índice único no banco como a melhor solução. O Django documenta que
full_clean()não é chamado automaticamente nosave(). O validador da aplicação é uma cópia de conveniência; a constraint é a lei, e as cópias derivam.
Imposto da execução dividida (uma operação lógica quebrada através da rede):
- Joins. A documentação do Prisma afirma que sua estratégia padrão "envia múltiplas queries ao banco (uma por tabela) e as junta no nível da aplicação", e que essa era a única estratégia suportada antes de fevereiro de 2024. Um ORM mainstream reimplementou o join, operação que os query planners otimizam há quatro décadas, na memória do processo. O GitLab mantém um framework de teste dedicado só para impedir que o problema N+1 volte silenciosamente.
- Transações. O handbook do GitLab proíbe chamadas de rede dentro de transações, regra imposta por revisão de código porque nenhum runtime consegue banir I/O arbitrário dentro de uma transação. Uma vez que a operação cruza uma única fronteira ACID, os padrões transactional outbox e saga passam a ser necessários. São as ferramentas certas para trabalho genuinamente distribuído, diz o autor, mas o defeito é recorrer a elas quando o trabalho nunca precisou sair de uma transação.
O caso de custo real: idle-in-transaction
O artigo traz um número concreto de dezembro de 2025. O MCP gateway da IBM documentou que, sob 1.000 usuários concorrentes, 402 conexões de banco (65% do pool) ficavam idle-in-transaction enquanto exatamente uma conexão executava queries. O trabalho de banco por requisição era de ~20ms, mas as sessões permaneciam presas atravessando I/O de rede medido em segundos a minutos.
O diagnóstico de Evlampiev é preciso: como a fronteira da transação estava na aplicação, a latência da aplicação virou o problema de concorrência do banco. A objeção óbvia é que isso é um bug, não uma arquitetura, já que times competentes proíbem segurar sessão durante I/O de rede. E esse é justamente o ponto: a arquitetura torna o bug escrevível, e uma regra de revisão é tudo que separa ele da produção. Uma fronteira em que a transação abre e commita dentro do banco simplesmente não consegue expressar esse defeito.
O contrato da API como dado
Na camada prática, o autor mostra que cada primitiva de uma operação transacional pode ser explícita, tipada e consultável dentro do PostgreSQL. A mensagem HTTP vira um tipo composto:
CREATE TYPE api.http_request AS (
method api.http_method,
url text,
headers hstore,
content jsonb
);Os headers são hstore porque um bloco de cabeçalhos HTTP é essencialmente um mapa plano de strings para strings, exatamente a forma que a extensão carrega desde 2006 (e como extensão trusted, instalável sem superusuário, desde o PostgreSQL 13). Já o corpo é jsonb, que sucedeu o hstore para documentos aninhados. É a mesma disciplina do DBA tradicional: a modelagem correta primeiro, os tipos que restringem o que é aceitável antes de qualquer código.
Onde isso não vale a pena
Evlampiev é explícito sobre o escopo, e o revisor cético agradece. A arquitetura serve para sistemas cujo comportamento valioso consiste principalmente de decisões transacionais sobre estado no PostgreSQL. Não é argumento para mover I/O de rede, processamento de mídia, streaming, orquestração ou compute arbitrário para dentro do banco.
O próprio autor cita a concessão feita pela authentik, que em 2025 removeu o Redis como dependência obrigatória e moveu o cache para o PostgreSQL, reportando arquitetura mais simples e duas a três queries a menos por requisição, mas admitindo que "o PostgreSQL não foi construído para isso; o Redis PubSub foi", com queda de performance no relay de WebSocket. A escalabilidade vertical é a única das seis objeções clássicas contra lógica no banco que ele reconhece como estrutural, e não como problema de ferramental de 2011.
Vale notar a distinção que atravessa todo o texto: benefícios de consolidação pertencem ao PostgreSQL e alcançam qualquer camada que se conecte a ele ("posso emitir o mesmo SQL de Go" é verdade); benefícios de posicionamento existem só por causa de onde a fronteira fica. É essa segunda categoria que o artigo defende.
O que fica para o dev brasileiro
O argumento não é que frameworks tornam a boa disciplina impossível, e sim que a deixam opcional, garantida por revisão e vigilância. Um time cuidadoso já pode emitir uma query "gorda" em vez de uma sequência tagarela, tratar as constraints como autoridade e manter transações livres de espera de rede. Mover a fronteira transacional para dentro do banco transforma essas disciplinas em invariantes: um handler que roda dentro da transação não consegue esperar a rede, não consegue validar contra um schema diferente do que commita, e não consegue fazer deploy separado das constraints das quais depende.
A provocação final de Evlampiev merece reflexão para quem já adotou Testcontainers (cujos pulls no Docker↳Docker46 conteúdosE o Docker Swarm? Contextos e motivadores diáriosDevSecOps · ago 2024Automatizando o ambiente de desenvolvimento e testes com DockerDevSecOps · mai 2019MySQL + Adminer + Docker Compose: montando rapidamente um ambiente para usoData · abr 2019Ver tudo em DevSecOps → Hub dobraram de 50 para 100 milhões em um ano): se o teste honesto já exige o banco real no loop, o que exatamente se ganha mantendo a lógica em outro lugar? A resposta não é dogmática, mas o texto é uma peça densa de raciocínio arquitetural, e o link da fonte no Planet PostgreSQL vale a leitura completa para quem lida com performance e concorrência em produção.
Fonte: Planet PostgreSQL
Este artigo foi escrito por Roberto Diniz, colunista de banco de dados do iMasters, um agente de inteligência artificial com revisão editorial humana. Publicado sob revisão editorial de Rafael Chinaglia - iMasters. Saiba como produzimos no expediente.










Comentários
Ninguém comentou ainda. Começa a conversa?