MassTransit com RabbitMQ é a escolha default de quem precisa de mensageria em .NET, e funciona. O que não funciona é o setup ingênuo: salvar o agregado no banco, publicar o evento logo depois, confiar que tudo deu certo. At-least-once delivery sem outbox é a receita exata para mensagens duplicadas, mensagens perdidas e estados inconsistentes que só aparecem em produção com carga real, quando o broker pisca por trezentos milissegundos no meio de uma transação.
O cenário clássico: o repositório salva o pedido, o commit acontece, o publish para o broker falha por timeout de rede. O agregado está no banco, o evento não saiu, os consumidores nunca souberam. Pior ainda: o publish acontece antes do commit, o broker entrega, o consumidor processa, e a transação do banco faz rollback. Agora o resto do sistema acredita em um estado que nunca existiu. Os dois cenários acontecem em ambiente real, e o segundo é o que destrói confiança em mensageria.
O outbox resolve isso transformando publicação em duas etapas. Na mesma transação do agregado, o handler grava o evento numa tabela de outbox no mesmo banco. Commit atômico: ou agregado e evento existem juntos, ou nenhum dos dois. Um worker dedicado lê a tabela em loop curto, publica no broker, marca como processado. Se o broker estiver fora, o worker tenta de novo até cair. Nenhuma mensagem é perdida, nenhuma é publicada sem o agregado existir.
MassTransit suporta outbox nativo a partir da versão 8 com EF Core, e essa é a configuração que deveria ser default. Mas habilitar a feature não resolve idempotência do lado do consumidor: at-least-once significa que a mensagem pode chegar duas vezes, e o consumidor precisa estar preparado. Chave de deduplicação no payload (id do evento, não id da mensagem do transport) e tabela de mensagens processadas no consumidor são obrigatórias. Sem isso, o outbox apenas garante entrega, não garante processamento único.
A confusão recorrente é equiparar outbox a Event Sourcing. Não é. Outbox resolve confiabilidade de publicação. Event Sourcing resolve fonte da verdade. Os dois podem coexistir (o event store é o próprio outbox), mas adotar Event Sourcing para resolver entrega de mensagem é mover uma montanha para abrir uma porta. Em sistema CRUD com agregado tradicional, outbox sobre o banco existente é a resposta certa, e custa duas tabelas e um worker.
Onde outbox não compensa: domínio puramente CRUD sem integração externa crítica, eventos de telemetria onde perda esporádica é aceitável, sistemas de notificação best-effort. Adicionar outbox nesses casos é overengineering: gasta polling no banco, adiciona latência de até o intervalo do worker, complica deploy. A pergunta que decide é direta: se essa mensagem for perdida silenciosamente, alguém vai descobrir? Se a resposta é sim, outbox. Se é não, publish direto está ótimo.
Tags
- #dotnet
- #arquitetura
- #mensageria