Marcio Cunha

Transações perdidas? Como desenhar arquiteturas Event-Driven

Descubra como garantir consistência eventual e gerenciar mensagens duplicadas em sistemas distribuídos complexos usando o padrão Sagas.

Marcio Cunha•4 min
Também disponível em:EnglishEspañol
Resumo
  • Sistemas distribuídos trocam transações atômicas por consistência eventual para ganhar escala e resiliência operacional.
  • O padrão Sagas divide processos complexos em etapas locais que podem ser revertidas caso ocorra falha no meio do fluxo.
  • Mensagens duplicadas exigem idempotência rigorosa para evitar que operações de pagamento ou cadastro rodem duas vezes.
  • A arquitetura orientada a eventos exige monitoramento contínuo para rastrear gargalos em tempo real e evitar falhas silenciosas.
  • Projetar fluxos resilientes protege o negócio contra perdas financeiras causadas por quedas repentinas de rede ou servidores.

Por que transações tradicionais quebram na nuvem?

Quando construímos sistemas corporativos monolíticos, confiar em bancos de dados relacionais e em transações ACID (um conjunto de regras que garante que tudo seja salvo ou nada seja alterado) parece a solução definitiva. Na prática, quando dividimos esse sistema em microsserviços espalhados por servidores diferentes, o modelo tradicional trava. Na nuvem, a rede falha, instâncias caem e a famosa consistência imediata desaparece, dando lugar a consistência eventual, onde os dados levam alguns segundos para se alinharem entre os serviços.

Arquiteturas orientadas a eventos resolvem o problema de escala ao permitir que diferentes serviços comuniquem-se de forma assíncrona, enviando avisos de que algo aconteceu. No entanto, essa liberdade traz um efeito colateral perigoso: o risco real de perder transações no meio do caminho ou processar a mesma mensagem duas vezes. Para evitar o caos, engenheiros precisam abandonar a ilusão de que o mundo distribuído funciona igualzinho a um único servidor local.

O desafio da consistência eventual em microsserviços

Imagine que um cliente faz uma compra em um e-commerce. O serviço de pagamento processa o cartão, mas o serviço de estoque cai logo em seguida antes de separar o produto. Em um banco tradicional, tudo seria cancelado com um clique. Em um sistema distribuído, o dinheiro já saiu da conta, mas o estoque continua intacto, gerando um descompasso financeiro grave.

A consistência eventual significa que os dados não estão sincronizados o tempo todo, mas vão se acertar em algum momento próximo. Para que isso funcione sem dor de cabeça, a engenharia de software recorre a padrões de design que aceitam o risco da falha temporária e criam mecanismos automáticos de recuperação. É aqui que entram estratégias como o padrão Sagas para coordenar etapas sem travar o banco de dados principal.

Como funciona o padrão Sagas na prática

O padrão Sagas substitui a transação gigante por uma sequência de transações locais em cada microsserviço envolvido. Cada passo atualiza o seu próprio banco e emite um novo evento para o próximo serviço da fila. Se tudo ocorrer bem, o fluxo termina com sucesso. Mas e se o terceiro serviço falhar? Em vez de dar um 'rollback' global que não existe na nuvem, o sistema executa transações compensatórias para desfazer o que foi feito passo a passo.

Se o estoque falhar após o pagamento ser aprovado, a Saga aciona automaticamente uma rotina para estornar o valor cobrado do cliente. Na prática, isso exige que cada operação de escrita tenha um inverso lógico bem desenhado. Não é um processo instantâneo, mas garante que o sistema inteiro volte a um estado coerente sem travar a operação dos outros clientes que continuam comprando no site.

O perigo invisível das mensagens duplicadas

Outro fantasma nas arquiteturas orientadas a eventos é a entrega duplicada de mensagens. Por causa de instabilidades na rede, um broker como Kafka ou RabbitMQ pode entregar o mesmo evento de cobrança duas vezes para o mesmo microsserviço. Se o microsserviço não estiver preparado, o cliente será cobrado em dobro pelo mesmo pedido.

A defesa contra esse problema chama-se idempotência — uma propriedade matemática que garante que executar a mesma operação dez vezes produz exatamente o mesmo resultado que executá-la uma única vez. Na prática, isso é implementado gravando um identificador único de cada transação em uma tabela de controle. Se a mensagem chegar novamente com o mesmo ID, o sistema apenas descarta o duplicado e retorna o sucesso anterior.

Garantindo a entrega confiável com o padrão Outbox

Para evitar que um evento se perca entre o momento em que o banco de dados é atualizado e o momento em que a mensagem é enviada para a fila, utilizamos o Transactional Outbox Pattern. Em vez de publicar o evento diretamente na rede, a aplicação grava o dado e o evento em uma mesma tabela transacional local. Um processo secundário lê essa tabela e despacha as mensagens com segurança para o barramento de eventos.

Essa estratégia garante que nenhum evento fique para trás, mesmo se o serviço cair bem na hora do disparo. Ao integrar essa prática com uma arquitetura orientada a leitura baseada em CQRS, conseguimos separar claramente quem grava os dados de quem apenas consulta, melhorando drasticamente a performance geral do sistema. Para aprofundar esses conceitos na prática em produção, confira também nossa análise técnica sobre IA alucinando? Como criar guardrails no LlamaIndex.

Considerações finais sobre resiliência distribuída

Desenhar arquiteturas orientadas a eventos exige uma mudança profunda de mentalidade: deixar de tentar evitar todas as falhas e passar a projetar sistemas que sobrevivem elegantemente a elas. Combinando consistência eventual, transações compensatórias via Sagas e checagens rigorosas de idempotência, sua engenharia ganha a robustez necessária para escalar sem perder dados no meio do caminho.