Em arquiteturas monolíticas, atualizar a base de dados e notificar outros componentes sobre uma alteração costuma ocorrer dentro do escopo de uma única transação ACID local. Se a gravação no banco falhar, nenhum evento é emitido; se o evento falhar, o banco executa o rollback automaticamente. Em uma arquitetura baseada em microsserviços e orientada a eventos (Event-Driven Architecture), essa simplicidade deixa de existir. Frequentemente, um serviço precisa realizar duas ações distintas em uma mesma operação de negócio: persistir o estado atual em seu banco de dados local e publicar um evento em um intermediário de mensagens (Message Broker como Apache Kafka ou RabbitMQ). Tentar executar essas duas gravações de forma independente na aplicação gera o clássico problema do Dual Write (Escrita Dupla). Se o banco de dados confirmar a gravação, mas a rede ou o broker falharem logo em seguida, o estado do banco ficará inconsistente com os demais serviços. Se a aplicação inverter a ordem e publicar o evento primeiro, mas a gravação no banco falhar, serviços externos processarão uma alteração que nunca existiu na fonte primária.
A Abordagem Tradicional: Escrita Dupla Direta na Aplicação Historicamente, a tentativa de resolver esse problema baseou-se na gravação sequencial direta realizada pelo código da aplicação, por vezes englobada em blocos de tratamento de exceções (try-catch) convencionais. O fluxo tradicional tenta persistir a entidade no banco de dados relacional ou NoSQL e, imediatamente no passo seguinte, chama a biblioteca do cliente de mensagens para publicar o evento na rede.
Limitações da Escrita Dupla Direta
- Inconsistência de Dados (In-doubt State): Falhas de rede, queda do contêiner ou indisponibilidade do broker entre as duas operações resultam em dados salvos no banco sem a devida notificação para o ecossistema;
- Falta de Atomicidade Distribuída: Não é possível agrupar a gravação no SGBD e a chamada de rede ao broker dentro de uma mesma transação transacional garantida;
- Complexidade na Aplicação: A camada de código de negócio passa a lidar diretamente com tratamento de retentativas e recuperação de falhas de infraestrutura de mensageria;
- Riscos em Ambientes Concorrentes: Falhas em retentativas síncronas podem gerar duplicação ou desordenamento no envio dos eventos.
A Abordagem Moderna: Transactional Outbox Pattern O padrão Transactional Outbox elimina o problema do Dual Write aproveitando a própria capacidade transacional do banco de dados local do microsserviço. Em vez de enviar o evento diretamente para o broker de mensagens durante o processamento da requisição, a aplicação grava a mensagem em uma tabela (ou coleção) auxiliar no mesmo banco de dados local, denominada Outbox Table, utilizando a mesma transação ACID que salva a entidade de negócio.
Como Funciona o Mecanismo
- Transação Única: A aplicação inicia uma transação no banco local, grava as alterações da tabela de negócio e insere o evento na tabela
Outbox. Ambas as gravações acontecem atomicamente: ou ambas são salvas, ou nenhuma é. - Separação de Responsabilidades: A requisição do usuário é concluída com sucesso imediatamente após a confirmação do banco de dados local.
- Publicação Assíncrona: Um componente separado (Publisher/Relay) lê os registros pendentes da tabela
Outboxe os publica no broker de mensagens. - Confirmação: Após a publicação ser confirmada pelo broker, o registro na tabela
Outboxé marcado como processado ou removido.
Estratégias de Implementação do Publisher (Relay) Existem duas abordagens predominantes para mover as mensagens da tabela Outbox para o broker de mensagens.
Polling Publisher (Worker de Consulta) Um processo em segundo plano (Worker/Cron) executa consultas periódicas (SELECT) na tabela Outbox em busca de eventos não processados, envia os dados para o broker e atualiza o status na tabela. Vantagens:
- Simplicidade de implementação utilizando linguagens e frameworks padrão;
- Funciona em qualquer banco de dados relacional convencional.
Desvantagens:
- Ineficiência e overhead no banco de dados devido a consultas constantes (polling);
- Maior latência na entrega do evento dependendo do intervalo de consulta;
- Risco de concorrência se existirem múltiplas instâncias do worker rodando simultaneamente.
Change Data Capture – CDC (Captura de Mudanças no Log) Ferramentas especializadas em CDC (como o Debezium) monitoram diretamente o log de transações do banco de dados (Transaction Log / WAL / Redo Log). Quando uma nova linha é inserida na tabela Outbox, a ferramenta captura a alteração diretamente do log e a envia para o broker sem consultar a tabela via SQL. Vantagens:
- Latência de publicação próxima do tempo real;
- Zero overhead de consultas SQL adicionais sobre o banco de dados;
- Desacoplamento total do processo da aplicação.
Desvantagens:
- Requer infraestrutura adicional e configurações avançadas no SGBD para acesso ao log transacional.
Quando Utilizar Cada Abordagem? Utilize Escrita Dupla Direta quando:
- O sistema for de pequeno porte e a eventual inconsistência entre o banco e as mensagens puder ser tolerada ou corrigida manualmente;
- A perda pontual de eventos não impactar os fluxos financeiros, regulatórios ou operacionais do negócio.
Utilize Transactional Outbox com Polling quando:
- A garantia de entrega das mensagens for obrigatória;
- O volume de transações for moderado e a infraestrutura for simplificada;
- Não for possível instalar componentes de CDC no banco de dados.
Utilize Transactional Outbox com Change Data Capture (CDC) quando:
- O sistema operar em alta escala e exigir publicação de eventos com baixíssima latência;
- A consistência de dados entre os microsserviços for um requisito crítico de arquitetura (At-least-once delivery);
- A arquitetura for fortemente orientada a eventos (Event-Driven) com brokers como o Apache Kafka.
Na prática, o padrão Transactional Outbox associado ao Change Data Capture tornou-se a solução definitiva para garantir resiliência e atomicidade na integração entre banco de dados e brokers de mensagens em arquiteturas Cloud Native.