Marcio Cunha

Implementação de Outbox Pattern com Change Data Capture e Debezium para Consistência Eventual

Descubra como garantir transações confiáveis e consistência eventual em microsserviços combinando o padrão Outbox, captura de dados de alteração e a ferramenta Debezium.

Marcio Cunha•5 min
Também disponível em:EnglishEspañol
Resumo
  • A tabela outbox resolve o problema de perda de eventos quando a base de dados e o broker de mensagens falham de forma desconexa
  • O Change Data Capture lê o log de transações do banco sem impactar a aplicação principal ou exigir consultas adicionais
  • O Debezium atua como um conector do Kafka Connect transformando alterações em tempo real em mensagens para mensageria
  • A idempotência no consumidor é indispensável para evitar duplicação de eventos gerados por reenvios e atrasos de rede
  • A complexidade operacional aumenta, exigindo monitoramento rigoroso do atraso na entrega e do crescimento do disco do banco

O Dilema da Consistência em Sistemas Distribuídos

Quando separamos um sistema monolítico grande em vários pedacinhos independentes, chamados de microsserviços, ganhamos liberdade para escalar e atualizar partes específicas sem derrubar o resto. Porém, criamos um problema espinhoso: como atualizar o banco de dados e avisar o resto do mundo sobre isso sem que um lado fique dessincronizado do outro. Na prática, imagine que você compra um ingresso de cinema: o pagamento é aprovado no seu banco e o assento precisa ser marcado no sistema do cinema instantaneamente. Se a luz acaba exatamente entre o pagamento e a marcação, o dinheiro sai da sua conta, mas você fica sem o lugar.

Em arquiteturas tradicionais, tentar salvar um registro no banco de dados e logo em seguida enviar uma mensagem para um canal de mensageria, como o Apache Kafka, costuma falhar. Se o banco aceita a transação, mas o Kafka cai logo depois, a mensagem se perde e o restante da empresa nunca fica sabendo que o evento aconteceu. Tentar fazer o inverso — enviar a mensagem primeiro e salvar depois — gera o problema oposto: avisamos que algo ocorreu antes mesmo de termos certeza de que foi salvo com segurança. Precisamos de um mecanismo que garanta que os dois mundos, banco de dados e mensageria, cheguem ao mesmo acordo mais cedo ou mais tarde, conceito conhecido como consistência eventual.

O Padrão Outbox como Ponte Segura

Para resolver essa falha de comunicação entre o banco e o sistema de mensagens, surgiu o padrão Outbox, que significa literalmente caixa de saída. A ideia central é simples e imita o mundo real: quando você quer enviar uma carta importante, você não corre até o correio imediatamente; você coloca a carta na sua caixa de correspondência na porta de casa, confiando que o carteiro passará mais tarde para recolhê-la. No software, em vez de disparar uma chamada de rede frágil para o broker de mensagens no mesmo momento em que o usuário clica em salvar, a aplicação grava a alteração do usuário e o evento que deve ser emitido dentro da mesma transação do banco de dados.

Isso significa que, se a transação do banco for bem-sucedida, a intenção de enviar o evento também estará guardada com total segurança na tabela outbox. Na prática, a aplicação realiza um insert na tabela principal (como pedidos) e outro insert na tabela auxiliar outbox na mesma fração de segundo, utilizando o mecanismo de transações atômicas que todo banco relacional moderno oferece. Se qualquer erro acontecer, tudo é desfeito e nada fica inconsistente. O grande desafio que sobra é: quem vai ler essa tabela de outbox e empurrar os dados para o mundo externo?

Change Data Capture com o Debezium

Aqui entra em cena o Change Data Capture, conhecido pela sigla CDC, que em tradução livre significa captura de dados de alteração. Na prática, o CDC é uma tecnologia que observa tudo o que acontece no banco de dados — inserções, atualizações e exclusões — lendo diretamente o arquivo de log de transações que o banco mantém para fins de recuperação em caso de pane. É como se instalássemos uma câmera de segurança invisível que registra cada modificação feita nas tabelas, sem que a aplicação precise fazer nenhuma consulta extra ou esforço adicional para avisar que algo mudou.

O Debezium é a ferramenta de código aberto mais popular para fazer esse trabalho pesado no ecossistema atual. Ele se conecta ao banco de dados (como PostgreSQL, MySQL ou Oracle) e traduz os registros brutos do log de transações em eventos padronizados, geralmente em formato JSON, enviando-os diretamente para uma plataforma de streaming como o Apache Kafka. Quando combinamos o padrão Outbox com o Debezium, eliminamos a necessidade de códigos complexos de varredura na aplicação. O Debezium lê a tabela outbox assim que os novos registros são confirmados pelo banco e os publica no broker de mensagens com baixíssima latência e alta confiabilidade.

Arquitetura e Fluxo de Execução

Para visualizar a engenharia dessa solução funcionando em produção, podemos traçar o caminho que um dado percorre desde o clique do cliente até a distribuição para outros serviços. O fluxo completo envolve a aplicação cliente, o banco de dados relacional, o conector de CDC e o barramento de eventos. Cada peça possui uma responsabilidade bem delimitada, garantindo que o sistema seja resiliente a falhas parciais e picos de tráfego repentinos.

  1. A aplicação recebe uma requisição e executa uma transação no banco de dados gravando a entidade de negócio e o evento correspondente na tabela outbox.
  2. O banco de dados grava a operação em seu log interno de transações para fins de auditoria e recuperação de falhas.
  3. O Debezium, rodando em um cluster Kafka Connect, lê continuamente esse log de transações em tempo real.
  4. O conector processa a linha inserida na tabela outbox, converte o conteúdo para um formato estruturado e publica no tópico correspondente do Kafka.
  5. Um processo de limpeza remove periodicamente os registros já processados da tabela outbox para evitar o crescimento descontrolado do banco.

Desafios Operacionais e Considerações Finais

Embora a combinação de Outbox Pattern, CDC e Debezium resolva o problema da consistência de dados com elegância, ela não elimina totalmente os trade-offs e a complexidade operacional. Na prática, como a leitura do log e a publicação dependem de redes e conexões que podem falhar momentaneamente, os consumidores das mensagens precisam ser construídos para lidar com entregas duplicadas, conceito conhecido como idempotência. Se um evento for processado duas vezes por engano, o sistema do cliente não pode cobrar o cartão de crédito duas vezes ou duplicar o envio de um produto.

Além disso, o monitoramento do espaço em disco da tabela outbox torna-se uma tarefa crítica para a equipe de engenharia. Se o cluster do Kafka cair por muitas horas, o Debezium parará de avançar na leitura do log e a tabela outbox crescerá rapidamente, podendo esgotar o armazenamento do banco de dados e derrubar a aplicação inteira. Apesar dessas responsabilidades adicionais de operação, adotar essa arquitetura elimina os temidos estados fantasma, garantindo que sua aplicação distribua eventos com precisão cirúrgica e total confiabilidade a longo prazo.