Voltar para o inícioMicroserviços & Mensageria

Como implementar Transactional Outbox em eventos de negócio: guia prático para produção

Guia prático para implementar Transactional Outbox em produção, com modelagem, publicação via polling ou CDC, idempotência, retries e observabilidade.

M

Max Alex

Como implementar Transactional Outbox em eventos de negócio: guia prático para produção

Em arquiteturas de microserviços, uma alteração de negócio frequentemente precisa produzir um evento para outros serviços. O problema é que salvar dados no banco e publicar no broker são operações distintas. Se uma delas falhar no momento errado, o sistema pode ficar inconsistente.

O Transactional Outbox resolve essa lacuna ao registrar a mudança de negócio e o evento pendente na mesma transação local. Depois do commit, um processo separado publica esse evento de modo recuperável. Este guia apresenta um desenho prático para levar o padrão à produção.

O problema: banco atualizado, evento não publicado

O cenário clássico é a dual write: a aplicação atualiza seu banco e, em seguida, envia uma mensagem ao broker. Entre essas duas ações existe uma janela de falha inevitável.

Imagine que um serviço confirma um pagamento, grava o novo status no banco e sofre uma queda antes de emitir PaymentConfirmed. O pagamento existe, mas o serviço de pedidos nunca é avisado. Um retry do produtor ajuda em falhas conhecidas, porém não resolve uma interrupção do processo depois do commit.

A comunicação por mensageria assíncrona reduz acoplamento entre serviços, mas exige uma estratégia confiável de publicação. Transações distribuídas entre banco e broker podem parecer uma solução, porém normalmente aumentam acoplamento, complexidade operacional e custo de recuperação.

O que é Transactional Outbox e quando usar

Transactional Outbox é um padrão em que o dado de negócio e um registro de evento são persistidos no mesmo banco, usando a mesma transação. Se o commit falhar, nenhum dos dois é salvo. Se funcionar, ambos ficam duráveis.

Um worker, conector ou processo de CDC lê os registros pendentes da outbox e os publica no broker. Caso o broker esteja indisponível, o evento continua armazenado e poderá ser reenviado depois.

O padrão é especialmente indicado para eventos relevantes ao domínio, integrações entre serviços, auditoria de mudanças e fluxos com consistência eventual. Ele não entrega exatamente uma vez de ponta a ponta; o contrato realista é entrega pelo menos uma vez, com consumidores preparados para reentregas.

Desenhe o contrato do evento antes da tabela outbox

Comece pelo evento, não pela infraestrutura. Um evento de negócio descreve algo que já aconteceu, por isso nomes no passado são mais claros: OrderCreated, OrderPaid e InvoiceIssued.

Um envelope consistente costuma incluir event_id, event_type, aggregate_id, occurred_at, version e correlation_id. O payload deve conter somente os dados necessários aos consumidores; não transforme o evento em um espelho do modelo interno ou em um canal para dados sensíveis.

Planeje evolução compatível desde a primeira versão. Campos novos devem ser opcionais quando possível, e mudanças incompatíveis merecem uma versão explícita. Os mesmos cuidados de versionamento de contratos ajudam a evitar que uma mudança de produtor interrompa vários consumidores.

{
  "event_id": "f3d2...",
  "event_type": "OrderPaid",
  "aggregate_id": "order-4821",
  "occurred_at": "2026-09-14T10:15:00Z",
  "version": 1,
  "correlation_id": "req-91ac",
  "data": {"amount": 249.90, "currency": "BRL"}
}

Modele a tabela outbox para recuperação e rastreabilidade

Uma tabela como outbox_events precisa permitir publicação, investigação e reprocessamento. Campos úteis incluem id, aggregate_type, aggregate_id, event_type, payload, headers, status, created_at, published_at, retry_count e next_attempt_at.

Use um UUID imutável como identificador global do evento. Armazene o payload serializado e cabeçalhos com metadados de rastreabilidade. Estados como PENDING, PROCESSING, PUBLISHED e FAILED facilitam a operação.

Crie índices voltados à consulta do publicador, por exemplo por status e next_attempt_at. Quando a ordem dentro de um agregado for importante, mantenha uma sequência por agregado ou publique usando aggregate_id como chave de partição.

Grave o dado de negócio e o evento na mesma transação

Este é o centro do padrão. Após validar o comando e aplicar a regra de domínio, a aplicação atualiza o agregado e insere o evento na outbox usando a mesma conexão e a mesma transação do banco.

BEGIN;
UPDATE orders SET status = 'PAID' WHERE id = :order_id;
INSERT INTO outbox_events (id, aggregate_id, event_type, payload, status)
VALUES (:event_id, :order_id, 'OrderPaid', :payload, 'PENDING');
COMMIT;

O evento só fica elegível para publicação após o commit. Evite enviar diretamente ao broker dentro dessa transação: se o envio for confirmado e o commit falhar, você terá publicado um fato que não existe no banco.

Mantenha a criação do evento próxima da regra que a originou. Assim, aprovar um cliente gera CustomerApproved no mesmo ponto em que o status é alterado, reduzindo o risco de eventos esquecidos em caminhos alternativos da aplicação.

Escolha a estratégia de publicação: polling ou CDC

Há duas abordagens principais. No polling publisher, um worker consulta periodicamente a outbox, reserva um lote de eventos, publica cada item e marca o resultado. É simples de implementar e oferece controle direto sobre lotes, locks e retentativas.

Em bancos que suportam concorrência adequada, o worker pode selecionar registros pendentes com locks de curta duração ou lease de processamento. Isso permite várias instâncias sem que todas publiquem o mesmo evento simultaneamente.

Com Change Data Capture, uma ferramenta lê o log de transações do banco e propaga alterações da tabela outbox. Essa abordagem reduz consultas ativas e pode ser uma boa evolução para maior volume, mas requer maturidade para operar conectores, schemas e retenção do log. Em cenários com Kafka, ferramentas como Debezium são uma escolha comum.

Comece com polling quando simplicidade e controle na aplicação forem prioritários. Avalie CDC quando volume, latência ou padronização de plataforma justificarem a complexidade adicional. Em ambos os casos, reprocessamento seguro continua obrigatório.

Trate duplicidades, ordenação e idempotência

Uma falha pode ocorrer depois que o broker aceitou a mensagem e antes que o publicador registre o evento como publicado. Na retomada, ele poderá reenviar a mesma mensagem. Essa é uma consequência normal da entrega pelo menos uma vez.

O produtor deve reduzir duplicidades desnecessárias com locks ou leases, mas a correção não pode depender apenas disso. Consumidores precisam ser idempotentes: processar o mesmo event_id duas vezes não pode gerar dois efeitos de negócio.

Uma técnica prática é manter uma tabela inbox com chave única em event_id. O consumidor grava o identificador junto da alteração que realiza; se receber o evento novamente, a restrição única transforma a reentrega em uma operação segura. Esse princípio se aplica também a consumidores idempotentes que expõem efeitos por APIs.

Quando a ordem for relevante, trate-a como requisito por agregado, não como promessa global do sistema. Use uma chave de partição baseada no agregado e não deixe um consumidor processar eventos concorrentes daquele mesmo fluxo sem uma política clara de sequência.

Projete retries, falhas permanentes e operação segura

Falhas transitórias, como indisponibilidade de rede ou broker, pedem retry com backoff exponencial e jitter. Registre a próxima tentativa em next_attempt_at para não pressionar uma dependência que já está degradada.

Falhas permanentes são diferentes: payload inválido, contrato incompatível ou destino inexistente não devem ser repetidos indefinidamente. Após um limite definido, marque o evento como FAILED ou encaminhe-o para uma fila de erro auditável.

O processo de replay precisa ser controlado: corrija a causa, registre quem solicitou o reenvio e preserve o histórico de tentativas. Também defina retenção para eventos publicados, arquivamento quando necessário e limpeza que não remova itens ainda exigidos por auditoria ou recuperação.

Monitore a outbox e valide o caminho para produção

Uma outbox sem observabilidade apenas troca perda silenciosa por acúmulo silencioso. Monitore a quantidade de eventos pendentes, a idade do evento pendente mais antigo, taxa de publicação, taxa de falha, número de retries e volume de itens em estado FAILED.

Propague correlation_id e trace_id do comando original até os consumidores. Isso permite relacionar uma ação de negócio ao evento, à publicação e aos efeitos posteriores.

Antes do rollout, teste cenários de falha deliberadamente: queda após o commit, indisponibilidade do broker, timeout depois do envio, duplicidade no consumidor e replay de evento. Defina um SLO de latência de publicação e um alerta, por exemplo, para quando o evento pendente mais antigo ultrapassar o limite aceitável ao negócio.

Checklist de implementação do Transactional Outbox

  • O dado de negócio e o registro da outbox são persistidos na mesma transação local.
  • O contrato possui identificador único, versão, horário de ocorrência e correlação.
  • Há uma estratégia explícita de polling ou CDC, com recuperação após falha.
  • Consumidores toleram reentrega e registram ou aplicam deduplicação por event_id.
  • Ordenação por agregado é tratada quando o domínio exige sequência.
  • Retries, backoff, falhas permanentes e replay têm regras operacionais definidas.
  • Métricas, alertas, logs e traces tornam a fila pendente visível.
  • Testes comprovam que uma interrupção entre commit e publicação não perde o evento.

Transactional Outbox não elimina a consistência eventual; ele a torna controlável. Ao aceitar a publicação assíncrona, persistir a intenção de emitir o evento e preparar consumidores para duplicidades, sua arquitetura passa a recuperar falhas sem perder fatos importantes do negócio.

Sua arquitetura precisa publicar eventos de negócio sem abrir espaço para inconsistências? Estruture uma avaliação técnica da sua mensageria e priorize os pontos de maior risco antes de escalar a operação.

M

Max Alex

Criador de conteúdo apaixonado por tecnologia e inovação. Acompanhe nossos artigos para ficar por dentro das melhores estratégias digitais.

Continue lendo