3.12

View in English

3.12 Arquitetura orientada a eventos e mensagens

Visão geral e motivação

A arquitetura orientada a eventos (EDA, event-driven architecture) é um estilo em que os componentes se comunicam produzindo e reagindo a eventos, e não chamando uns aos outros diretamente. Um evento é um fato: algo que já aconteceu, como “PedidoRealizado” ou “PagamentoCapturado”. Um produtor anuncia o fato e segue em frente, e qualquer número de consumidores reage no seu próprio ritmo, sem que o produtor saiba quem está ouvindo. É uma postura diferente das chamadas de requisição e resposta do capítulo 2.3, em que um chamador pede a um serviço específico que faça algo e espera a resposta.

Para uma grande organização, o apelo é o desacoplamento em escala. Quando há dezenas de equipes e centenas de serviços, ligar tudo com chamadas diretas ponto a ponto produz uma teia frágil em que a mudança de uma equipe quebra a de outra e ninguém consegue rastrear por quê. Os eventos permitem que as equipes se integrem por um fluxo compartilhado de fatos e não pelos internos umas das outras, e um novo consumidor entra assinando, sem que o produtor mude uma linha de código. Essa propriedade, mais que a vazão bruta, é o motivo pelo qual as abordagens orientadas a eventos continuam se espalhando por empresas que substituem integrações emaranhadas e por governos que juntam agências que possuem, cada uma, seus próprios sistemas.

O setor público ganha um segundo benefício, fácil de subestimar: um registro durável e ordenado do que aconteceu é um ativo de auditoria e de transparência. Quando um cidadão pergunta por que uma decisão de benefício saiu do jeito que saiu, um log imutável dos eventos que levaram a ela responde diretamente. Mas o design orientado a eventos não é gratuito nem sempre certo: os fluxos assíncronos são mais difíceis de rastrear, mais difíceis de raciocinar e fáceis de aplicar em excesso. Este capítulo tem opinião sobre quando o desacoplamento e a escala compensam a complexidade adicional, e quando uma simples chamada síncrona teria servido melhor. Ele se apoia nas realidades de sistemas distribuídos do capítulo 3.3, então leia esse primeiro se ainda não o fez.

Princípios fundamentais

  • Eventos são fatos, não instruções. Um evento diz o que aconteceu. Um comando pede que algo aconteça. Mantenha-os distintos e nomeie os eventos no passado.
  • O desacoplamento é o ponto. Os produtores não devem saber nem se importar com quem consome seus eventos. Se se importam, vocês têm acoplamento fantasiado de mensageria.
  • Projete para a entrega pelo menos uma vez. A entrega exatamente uma vez é um mito. Torne todo consumidor idempotente para que as duplicatas sejam inofensivas.
  • A ordem é uma garantia que você paga. Você obtém ordenação dentro de uma partição, não entre um tópico inteiro. Escolha as chaves de partição deliberadamente.
  • O esquema é o contrato. A forma de um evento é uma interface pública. Evolua-a com o cuidado que teria com uma API publicada.
  • Assíncrono não significa inobservável. Se você não consegue seguir uma mensagem de ponta a ponta, não consegue operar o sistema.
  • A complexidade precisa ser merecida. A event sourcing, o CQRS e as sagas são poderosos e caros. Recorra a eles quando o problema exigir, não por padrão.

Recomendações

Distinga eventos, comandos e mensagens antes de construir qualquer coisa

Essas três palavras são usadas de forma intercambiável, e a confusão causa erros reais de design. Um comando é um pedido para fazer algo (“CapturarPagamento”), dirigido a um tratador, e pode ser rejeitado. Um evento é uma notificação de que algo já aconteceu (“PagamentoCapturado”), transmitida a quem tiver interesse, e não pode ser rejeitado porque o fato já é verdadeiro. Uma mensagem é o envelope neutro que carrega qualquer um dos dois pelo fio. A distinção molda o acoplamento: os comandos acoplam o emissor a um receptor e a um resultado específicos, enquanto os eventos abrem mão do controle sobre o que acontece depois. Nomeie os seus eventos no passado e, quando se pegar publicando um “evento” que na verdade significa “por favor, vá fazer esta coisa específica”, vocês escreveram um comando disfarçado.

Escolha filas, logs e publicação/assinatura de propósito

Nem toda mensageria tem a mesma forma, e escolher a errada é um erro inicial comum. Uma fila de mensagens entrega cada mensagem a um consumidor e normalmente a remove depois de processada, o que serve à distribuição de trabalho: muitos trabalhadores puxando tarefas, cada uma feita uma vez. Um log de eventos durável (um stream) mantém os eventos em ordem e permite que muitos consumidores independentes leiam no seu ritmo, reproduzindo o histórico a partir de qualquer ponto, o que serve à distribuição de eventos e à auditoria. A publicação/assinatura (publish/subscribe) faz os produtores publicarem num tópico e cada assinante receber a sua própria cópia. A regra prática: se a mensagem é uma tarefa que um trabalhador deve completar, recorra a uma fila. Se é um fato que muitas partes podem querer agora ou depois, recorra a um log durável, que também dá reprodução para recuperação e para integrar novos consumidores. Veja o capítulo 3.4 para como essas escolhas interagem com a sua estratégia de armazenamento de dados.

Prefira a coreografia para a autonomia e a orquestração para o controle

Quando um processo de negócio abrange vários serviços, vocês o coordenam de duas maneiras. Na coreografia, cada serviço reage a eventos e emite os seus, sem cérebro central: ao máximo desacoplada e boa para a autonomia das equipes, mas o processo geral só existe como comportamento emergente que nenhum lugar único descreve. Na orquestração, um coordenador central conduz os passos e conhece o fluxo inteiro: mais fácil de monitorar e de modificar, ao custo de um componente de que todo passo depende. Um bom padrão é a coreografia para reações fracamente relacionadas (“quando um pedido é enviado, o serviço de fidelidade concede pontos”) e a orquestração para uma transação definida, com condição clara de sucesso e necessidade de relatar o status. Não deixem um processo importante viver apenas como conhecimento tribal espalhado por dez tratadores de eventos.

Recorra à event sourcing e ao CQRS só quando compensarem

A event sourcing guarda o estado como uma sequência de eventos só de acréscimo e não como um retrato atual que você sobrescreve, e você reconstrói o estado atual reproduzindo-os. A vantagem é uma trilha de auditoria perfeita, a capacidade de reconstruir qualquer estado passado e consultas temporais. O custo é que vocês versionam esquemas de eventos para sempre, tratam reprodução e snapshots e carregam um modelo mental que a maioria dos desenvolvedores nunca usou. O CQRS (Command Query Responsibility Segregation, segregação de responsabilidade entre comandos e consultas) separa o modelo de escrita de um ou mais modelos de leitura para que leituras e escritas escalem e evoluam de forma independente. Combina naturalmente com a event sourcing mas não a exige. Ambos brilham em domínios com necessidades genuínas de auditoria, conformidade ou consultas complexas, que é por que as finanças reguladas e o governo acham que valem o trabalho. Para um simples serviço de criar-ler-atualizar-excluir são complexidade acidental de que vocês se arrependerão, então apliquem-nos à fatia do domínio que precisa deles e não ao sistema inteiro por reflexo.

Gerencie transações distribuídas com sagas, não com commit em duas fases

Em geral não se consegue envolver uma transação atômica em vários serviços e bancos de dados. O commit em duas fases distribuído segura travas pela rede, reduz a disponibilidade e escala mal, então raramente cabe num sistema orientado a eventos. O padrão saga o substitui: modele a transação como uma sequência de transações locais, cada uma emitindo um evento que dispara a seguinte, e dê a cada passo uma ação compensatória que o desfaz se um passo posterior falhar. Se “reservar estoque” tem sucesso mas “cobrar cartão” falha, uma compensação libera o estoque. As sagas podem ser coreografadas ou orquestradas, e a orquestração costuma vencer para tudo que vocês precisam monitorar. Como as sagas abraçam a consistência eventual, o sistema passa por estados intermediários (“reservado mas não pago”) antes de convergir, então projetem a experiência do usuário e a trilha de auditoria para mostrar com honestidade os estados “em andamento” e “compensado”. O capítulo 3.3 cobre o mesmo terreno pelo ângulo dos sistemas distribuídos.

Projete para a entrega pelo menos uma vez e torne os consumidores idempotentes

Os sistemas de mensagens não conseguem de fato entregar exatamente uma vez diante de falhas, porque a confirmação que diz “processei isto” pode ela mesma se perder, forçando uma reentrega. O que se consegue é a entrega pelo menos uma vez com processamento idempotente, que produz efeitos exatamente uma vez. A idempotência significa que processar o mesmo evento duas vezes deixa o mesmo resultado que processá-lo uma. Chega-se lá com uma chave de idempotência em cada evento e um registro do que já foi tratado, para que uma duplicata seja reconhecida e descartada. A entrega no máximo uma vez (dispare e esqueça, sem reentrega) é mais simples mas perde mensagens em silêncio, então reserve-a para dados que vocês podem se dar ao luxo de perder. Tratem com suspeita o rótulo “exatamente uma vez” que alguns fornecedores anunciam: em geral significa exatamente uma vez dentro da fronteira de um sistema sob condições específicas, não a garantia de ponta a ponta que a frase sugere.

Controle a ordenação com partições e conheça os seus grupos de consumidores

A ordenação não é global nem gratuita: é local e paga. Um stream é dividido em partições, e vocês obtêm ordenação dentro de uma partição, não entre o tópico. Os eventos são roteados para uma partição por uma chave de partição, então escolher essa chave é como se controla o que permanece ordenado: usando o ID do cliente como chave, os eventos de um cliente permanecem em ordem entre si, enquanto clientes diferentes são processados em paralelo. Os grupos de consumidores permitem que um conjunto de trabalhadores divida as partições de um tópico, cada partição tratada por um trabalhador, que é como se escala a vazão preservando a ordem por partição. É aqui que a escalabilidade encontra a correção, ligando ao capítulo 3.5. Escolham uma chave de partição que reflita o seu requisito real de ordenação e espalhe a carga de maneira uniforme, porque uma chave que afunila a maior parte do tráfego numa partição cria um ponto quente que nenhum número de trabalhadores alivia.

Trate os esquemas como contratos, com um registro e regras de evolução

A estrutura de um evento é uma interface publicada, consumida por equipes que vocês talvez nunca conheçam, então mudá-la sem cuidado as quebra à distância. Ponha os seus esquemas de eventos num registro de esquemas, um catálogo compartilhado que guarda cada esquema e impõe regras de compatibilidade quando um produtor tenta alterar um. Adotem uma política explícita: as mudanças retrocompatíveis (acrescentar um campo opcional) são permitidas. As mudanças que quebram (remover um campo, mudar um tipo, renomear) exigem uma nova versão do esquema e um plano de migração. Isso permite que os produtores evoluam sem uma implantação sincronizada em todos os consumidores, que é o motivo inteiro de vocês terem escolhido eventos. A mesma disciplina de versionamento de interfaces do capítulo 2.3 se aplica, porque um esquema de evento é uma API com outro nome.

Garanta a entrega com o outbox transacional e trate as falhas explicitamente

Um bug clássico: o seu serviço grava no banco de dados e depois publica um evento, e cai entre os dois, de modo que o banco mudou mas o evento nunca saiu. O outbox transacional corrige isso gravando o evento numa tabela de outbox na mesma transação de banco de dados que a mudança de estado, para que os dois confirmem ou falhem juntos. Um relé separado então lê o outbox e publica no broker, muitas vezes usando a captura de dados de mudança para acompanhar o log do banco. Para as falhas de consumo, uma fila de mensagens mortas (dead letter queue) guarda as mensagens que falham repetidamente, para que uma mensagem envenenada (uma que nunca terá sucesso, talvez por estar malformada) não bloqueie para sempre a fila atrás dela. Acrescentem a contrapressão para que um produtor rápido não afogue um consumidor lento: limitem as filas e desacelerem ou descartem carga quando encherem, em vez de esgotar a memória. Esses quatro mecanismos separam uma demonstração de um sistema que vocês conseguem operar às 3 da manhã.

Torne os fluxos assíncronos observáveis de ponta a ponta

O custo mais difícil de adotar a orientação a eventos é que uma única ação de negócio agora se espalha por produtores, brokers e consumidores sem pilha de chamadas ligando-os. Propaguem um ID de correlação por todo evento para poderem seguir um fluxo lógico por cada salto, a mesma disciplina que o capítulo 3.3 prescreve para chamadas síncronas. Acompanhem a defasagem do consumidor (consumer lag, o quanto cada consumidor está atrasado em relação ao tempo real) como métrica de primeira classe, porque a defasagem crescente é o seu primeiro aviso de problema, e monitorem a profundidade da fila de mensagens mortas, a latência de processamento e as taxas de reentrega. Sem isso, um evento que silenciosamente deixa de ser consumido vira um bug invisível que aparece dias depois como dados faltando.

Compromissos: prós e contras

AbordagemPrósContras / custo
Requisição/resposta síncronaSimples de raciocinar, resultado imediato, rastreamento fácilAcoplamento temporal forte, falhas em cascata, escala limitada
Orientada a eventos (pub/sub sobre um log)Desacoplamento, escala independente, reprodução, trilha de auditoriaConsistência eventual, rastreamento mais difícil, mais peças móveis
Fila de mensagens (distribuição de trabalho)Nivelamento de carga, bufferização, amigável à contrapressãoUm consumidor por mensagem, menos adequada para difusão
Event sourcing + CQRSHistórico completo, consultas temporais, leitura/escrita escalam de forma independenteVersionamento de esquemas para sempre, complexidade de reprodução, curva de aprendizado íngreme
Saga (em vez de commit em duas fases)Escalável, disponível, sem travas distribuídasConsistência eventual, lógica de compensação, mais difícil de raciocinar

A tensão central é entre o desacoplamento e a compreensibilidade. Cada evento que vocês acrescentam afrouxa o acoplamento entre produtor e consumidor, comprando autonomia das equipes e escala independente e, ao mesmo tempo, remove uma linha da história que uma chamada síncrona teria contado com clareza: o fluxo se torna emergente, vivendo nas interações e não em algum arquivo. Resolvam isso sendo seletivos: usem eventos onde o desacoplamento genuinamente compensa, como a integração entre fronteiras de equipes, a distribuição para muitos consumidores, o amortecimento de picos de carga e a auditoria. Mantenham as chamadas síncronas onde precisam de uma resposta imediata e de um modelo mental simples, como ler dados para renderizar uma página. O modo de falha a evitar é transformar cada chamada de função interna num evento e chamar isso de arquitetura, o mesmo juízo arquitetural que o capítulo 3.2 pede a todo padrão que vocês adotam.

Perguntas para discutir com sua equipe

  1. Para esta interação específica, de fato precisamos de um evento, ou uma chamada síncrona seria mais clara e mais segura? Pular essa pergunta é como um sistema acumula complexidade acidental. O teste honesto é se o produtor precisa de um resultado de volta agora (uma chamada) ou está anunciando um fato a que outros podem reagir no seu tempo (um evento). Levem a interação específica, não uma preferência geral, e perguntem que desacoplamento ganham e que clareza de rastreamento perdem. Se o chamador bloqueia esperando o “evento” ser processado, vocês construíram uma chamada síncrona lenta e difícil de depurar e pagaram a mais pelo privilégio. O padrão para interações internas, da mesma equipe e que precisam da resposta na hora deve ser uma chamada direta. Reservem os eventos para onde o acoplamento frouxo merece o seu custo.

  2. O que acontece quando um consumidor recebe o mesmo evento duas vezes, e nós de fato testamos? A entrega pelo menos uma vez garante que haverá duplicatas, então todo consumidor deve ser idempotente, mas a idempotência é fácil de afirmar e fácil de errar. Percorram um consumidor real e rastreiem exatamente como uma segunda entrega é reconhecida e neutralizada, seja por uma chave de idempotência, um log de eventos processados ou uma operação naturalmente idempotente. Levem os resultados de um teste de verdade em que vocês reentregam um lote e confirmam que não houve cobranças em dobro, registros duplicados nem notificações repetidas. Prestem atenção especial aos efeitos colaterais que saem do seu banco de dados, como e-mails, pagamentos e chamadas a terceiros, porque é aí que os bugs de não idempotência atingem diretamente os clientes. Se a sua equipe não consegue apontar um teste que prove a segurança contra duplicatas, presumam que não estão seguros.

  3. Quando um fluxo de eventos quebra em produção, quanto tempo até percebermos, e conseguimos rastrear uma mensagem de ponta a ponta? As falhas assíncronas são silenciosas, então um consumidor que para de processar em silêncio pode passar despercebido até que dados faltando virem uma reclamação de cliente ou uma lacuna de auditoria. Perguntem qual é o sinal mais precoce de vocês e se monitoram a defasagem do consumidor e a profundidade da fila de mensagens mortas como métricas de alerta e não painéis que ninguém olha. Levem um incidente real ou um exercício de dia de simulação e cronometrem quanto leva para seguir um único ID de correlação pelo produtor, pelo broker e por todo consumidor. Se a resposta é “damos grep em vários serviços e chutamos”, a observabilidade de vocês não está pronta para a complexidade que assumiram. Em setores regulados, conseguir reconstruir exatamente como uma mensagem se moveu muitas vezes é uma exigência de conformidade, não um luxo.

  4. Como vamos evoluir um esquema de evento que muitas equipes já consomem sem quebrar nenhuma delas? A forma de um evento é um contrato público, e quando dezenas de consumidores dependem dela, uma mudança de aparência inocente pode quebrar sistemas de que vocês nunca ouviram falar, à distância, sem compilador para avisar. A tensão é real: os produtores querem se mover depressa e limpar seus eventos, enquanto todo consumidor quer a forma congelada para sempre, então combinem de antemão quais mudanças são seguras (acrescentar um campo opcional) e quais exigem uma nova versão e uma janela de migração (remover um campo, mudar um tipo, renomear). Levem a lista real de consumidores por tópico, se um registro de esquemas impõe hoje regras de compatibilidade ou se as formas mudam por acordo informal e por quanto tempo duas versões podem rodar em paralelo durante uma migração. Numa grande empresa ou num arranjo governamental de compartilhamento de dados entre agências, um esquema quebrado em silêncio pode corromper registros em sistemas que vocês não possuem e aparecer depois como falha de auditoria, então tratem a imposição de compatibilidade como governança, não como gentileza.

  5. Para a nossa transação de vários serviços mais importante, o que cada ação compensatória de fato desfaz, e que estados intermediários usuários e auditores verão? As sagas trocam a ilusão reconfortante de uma transação atômica por uma sequência de passos locais que podem falhar cada um, então o sistema de fato passa por estados como “reservado mas não pago” e “cobrado mas não enviado” antes de convergir, e fingir o contrário é como se entrega uma saga que vaza dinheiro ou deixa registros órfãos. Percorram o processo real de ponta a ponta, nomeiem a compensação de cada passo (o que libera o estoque, o que estorna o cartão) e decidam se uma saga orquestrada que vocês conseguem monitorar vence uma coreografia emergente que nenhum lugar único descreve. Levem os casos de falha que vocês de fato testaram, não o caminho feliz, e confirmem que a experiência do usuário e a trilha de auditoria mostram os estados “em andamento” e “compensado” com honestidade em vez de escondê-los. Em sistemas de finanças, benefícios ou tributos, um regulador perguntará como era o registro em cada momento intermediário e quem era responsável pela compensação, então os estados da saga são eles mesmos um artefato de conformidade.

  6. Quem opera o broker ou o log de eventos, e contamos o custo real de operá-lo contra uma alternativa gerenciada? A espinha dorsal de mensageria não é infraestrutura gratuita que aparece quando vocês a desenham num diagrama: alguém a corrige, escala suas partições, ajusta a retenção, responde quando ela chama às 3 da manhã e é dono de sua capacidade e de seus modos de falha. Decidam deliberadamente entre hospedar um broker de código aberto e comprar um serviço gerenciado, pesando controle e residência de dados contra o ônus operacional e o custo de licença, e sejam honestos sobre se a equipe tem a profundidade para operar bem um log distribuído. Levem o custo total de propriedade: a carga de sobreaviso, as habilidades especializadas exigidas, a conta de retenção e de armazenamento e o que uma queda do broker faz a todo fluxo dependente. Para uma empresa, a resposta molda o mandato de uma equipe de plataforma, e para um órgão governamental as regras de contratação, as exigências de soberania de dados e uma necessidade forte de evitar o aprisionamento ao fornecedor podem prevalecer sobre a opção mais barata, então tragam essas restrições à tona antes de se comprometerem com uma tecnologia que operarão por uma década.

Perspectiva por setor

Startup. O seu recurso mais escasso é a atenção da engenharia, então fiquem síncronos até a distribuição realmente doer. Quando três coisas precisam reagir a uma ação, publiquem um único evento (como “PedidoRealizado”) numa fila ou log gerenciado em vez de levantar o seu próprio cluster de broker, e mantenham o pagamento e tudo de que precisam de resposta imediata como chamada direta. Não adotem event sourcing, CQRS nem sagas para parecer sofisticados. Essa complexidade ultrapassará uma equipe minúscula e desacelerará a própria iteração em que vocês competem.

Pequena empresa. Vocês não têm especialista em mensageria nem vontade de rodar Kafka, então tratem a integração assíncrona como algo que se compra, não que se opera. Apoiem-se nos eventos que as suas ferramentas existentes já emitem (webhooks, as filas embutidas no seu provedor de nuvem ou nas plataformas SaaS) e deixem um serviço gerenciado ser dono da entrega, da retenção e da infraestrutura de idempotência. Enquadrem a escolha como comprar versus construir com honestidade: um punhado de tratadores de webhook confiáveis vence um broker sob medida que vocês não conseguem operar nem depurar às 3 da manhã.

Grande empresa. O seu problema é o custo de integração entre muitas equipes, então o ganho é uma plataforma compartilhada e governada: um log de eventos durável, um registro de esquemas com política de compatibilidade imposta, propriedade clara dos tópicos e padrões comuns de idempotência e de outbox para que cada equipe não os reinvente. Este é o cenário em que substituir um barramento corporativo de serviços frágil ou uma teia de vínculos ponto a ponto se compõe em economia real, e em que sagas orquestradas com status visível permitem à equipe de operações gerenciar processos de vários passos. Orcem explicitamente o investimento em observabilidade e em governança de esquemas, porque nessa escala uma falha silenciosa de consumidor vira dados faltando em dezenas de sistemas.

Governo. Um log de eventos durável e ordenado é um ativo de auditoria e de transparência: responde “por que esta decisão saiu deste jeito” com uma sequência imutável de fatos, então apoiem-se na event sourcing onde a responsabilização o exigir. O compartilhamento de dados entre agências precisa de entrega pelo menos uma vez com deduplicação por um ID de evento estável, para que um registro reentregue nunca crie um caso duplicado, e a contratação deve pesar a soberania de dados, a divulgação das limitações de um broker e a portabilidade contra o aprisionamento. Publiquem, quando apropriado, como o fluxo funciona e como um cidadão pode contestar um resultado automatizado, e mantenham o log de eventos como o registro defensável que os órgãos de supervisão pedirão para inspecionar.

Exemplos

Startup. Uma pequena startup de comércio eletrônico começa com um fluxo síncrono: o checkout chama o serviço de pagamento e espera. À medida que cresce, quer que os e-mails de confirmação de pedido, as atualizações de estoque e um programa de fidelidade reajam às compras, e ligar cada um como mais uma chamada síncrona dentro do checkout torna o checkout lento e frágil. A equipe publica um único evento “PedidoRealizado” num log durável e deixa três consumidores independentes reagirem, de modo que o checkout fica rápido de novo e acrescentar uma quarta reação depois não exige mudança nele. Eles mantêm a captura do pagamento síncrona, porque precisam da resposta sim ou não antes de confirmar o pedido, que é exatamente a linha certa a traçar no tamanho deles.

Grande empresa. Uma seguradora global está se afogando em integrações ponto a ponto e num barramento corporativo de serviços (ESB) envelhecido, um hub central por onde todo sistema roteia e que virou um gargalo e um ponto único de falha. Ela migra para um log de eventos durável em que cada domínio publica seus fatos (apólice emitida, sinistro registrado, pagamento feito) e as equipes consumidoras assinam o que precisam. Uma saga de sinistros, orquestrada para que a equipe de operações veja o status de cada sinistro, coordena a liquidação de vários passos, com compensações para os passos que falham. Um registro de esquemas permite à equipe de apólices evoluir seus eventos sem uma implantação sincronizada em quarenta sistemas consumidores, precisamente a fragilidade que o antigo ESB impunha.

Governo. Uma autoridade tributária nacional precisa dar a cidadãos e auditores uma resposta defensável para “por que a minha apuração saiu assim”. Ela modela o domínio da apuração com event sourcing, de modo que toda mudança é um evento imutável num log ordenado e a apuração atual é uma reprodução desses eventos. Quando um cidadão contesta um valor, um servidor reconstrói o estado exato em qualquer data passada e mostra a sequência de fatos que o produziu. O compartilhamento de dados entre agências corre por tópicos duráveis com entrega pelo menos uma vez, e o consumidor de cada agência deduplica por um ID de evento, de modo que um registro reentregue nunca cria um caso duplicado. O log de eventos faz as vezes da trilha de auditoria que os órgãos de supervisão exigem, transformando uma obrigação de conformidade em subproduto do design.

Justificativa de negócio: motivações, ROI e TCO

O retorno da arquitetura orientada a eventos é dominado por uma coisa: o custo de integração ao longo do tempo. A integração ponto a ponto faz esse custo crescer com o número de conexões, que cresce mais depressa que o número de sistemas, então a integração vira o imposto que devora a sua capacidade de entrega. Os eventos achatam isso, porque as equipes se integram por um fluxo compartilhado de fatos, um novo consumidor entra assinando e os produtores evoluem atrás de um esquema versionado, de modo que o custo marginal da próxima integração cai bruscamente. Essa é a história central de ROI a contar à liderança: não o desempenho bruto, mas a redução cumulativa do custo de mudança entre muitas equipes.

Nomeiem o custo total de propriedade com honestidade para serem acreditados. Vocês assumem infraestrutura de broker para operar, governança de esquemas para manter e uma curva de aprendizado operacional mais íngreme, porque os sistemas assíncronos são genuinamente mais difíceis de depurar, então orcem de antemão o investimento em observabilidade. Pesem isso contra o custo de não adotar eventos onde cabem: uma camada de integração ossificada em que toda mudança é um projeto de coordenação entre várias equipes, um ESB legado que virou um gargalo em que ninguém ousa mexer e a incapacidade de acrescentar capacidades sem perturbar as antigas. Para a empresa que substitui uma fiação ponto a ponto frágil e para o governo que constrói trilhas de auditoria duráveis, o ganho é mais forte onde as necessidades de auditoria e de desacoplamento são reais. Onde essas necessidades estão ausentes, a resposta honesta é que um design síncrono mais simples tem o menor TCO, e vocês devem dizê-lo.

Antipadrões e armadilhas

  • O monolito distribuído disfarçado. Serviços que precisam ser todos implantados juntos e dependem dos eventos internos uns dos outros. Vocês acrescentaram um broker mas mantiveram o acoplamento, então têm as desvantagens dos dois estilos.
  • Eventos como comandos. Publicar “eventos” que na verdade significam “por favor, faça esta coisa específica para mim”, recriando o acoplamento forte com latência extra e pior rastreabilidade.
  • Presumir a entrega exatamente uma vez. Consumidores que quebram, cobram em dobro ou duplicam registros quando o broker inevitavelmente reentrega uma mensagem.
  • Event sourcing em tudo. Aplicar event sourcing e CQRS a domínios simples de criar-ler-atualizar-excluir que nunca precisaram de histórico, comprando complexidade íngreme sem benefício.
  • Nenhuma governança de esquemas. Produtores mudando livremente as formas dos eventos, quebrando consumidores a jusante à distância, sem verificações de compatibilidade.
  • Ignorar o outbox. Gravar no banco de dados e publicar um evento como dois passos separados, de modo que uma queda entre eles perde eventos em silêncio ou emite eventos fantasmas.
  • Nenhum tratamento de mensagens mortas. Uma única mensagem envenenada bloqueando uma partição, ou mensagens que falharam sumindo sem fila para capturá-las e inspecioná-las.
  • Fluxos invisíveis. Processamento assíncrono sem IDs de correlação, sem alertas de defasagem do consumidor e sem rastreamento, de modo que as falhas ficam silenciosas até virarem dados faltando.

Modelo de maturidade

  • Nível 1, Iniciar: A integração é feita de chamadas ponto a ponto ad hoc, ou existe um broker mas é usado como requisição/resposta síncrona. As duplicatas quebram os consumidores, não há disciplina de esquemas nem rastreamento entre saltos e as mensagens que falham desaparecem em silêncio.
  • Nível 2, Desenvolver: Um broker ou log está em uso real em alguns fluxos, e algumas equipes têm práticas básicas: os consumidores estão se tornando idempotentes e as filas de mensagens mortas pegam algumas falhas. Mas a abordagem é inconsistente entre equipes, eventos e comandos ainda estão confusos, os esquemas mudam por acordo informal e a observabilidade é fraca.
  • Nível 3, Padronizar: Eventos, comandos e mensagens são distinguidos deliberadamente em toda a organização. Os consumidores são idempotentes por padrão, os esquemas vivem num registro com uma política de compatibilidade documentada e imposta e o outbox transacional garante a entrega. As sagas com compensações tratam transações de vários serviços, os IDs de correlação se propagam por todo salto e esses padrões estão escritos e aplicados de forma consistente, e não deixados a cada equipe.
  • Nível 4, Gerenciar: A plataforma de eventos é medida e controlada com dados em relação a linhas de base. A defasagem do consumidor, a profundidade da fila de mensagens mortas, as taxas de reentrega e a latência de processamento são acompanhadas como métricas de alerta com limiares acordados, não painéis que ninguém olha. A compatibilidade de esquemas é verificada automaticamente antes que um produtor possa entregar uma mudança, e a reprodução, o tratamento de falhas e a idempotência são testados numa cadência fixa em vez de esperados. Decide-se com base em evidências se uma dada interação deve ser um evento ou uma chamada síncrona, e a capacidade, o custo de retenção e a confiabilidade de cada broker são revisados contra metas.
  • Nível 5, Orquestrar: Os estilos orientado a eventos e síncrono são escolhidos por interação como segunda natureza, e a event sourcing e o CQRS são aplicados precisamente onde as necessidades de auditoria e de consulta os justificam e em nenhum outro lugar. Os fluxos assíncronos são tão observáveis quanto os síncronos, a plataforma melhora continuamente conforme as necessidades mudam (aposentando tópicos mortos, evoluindo a governança de esquemas, reequilibrando partições) e a mensageria é integrada à arquitetura mais ampla e à estratégia de auditoria, de modo que a organização adapta o design de eventos conforme o negócio e suas obrigações mudam.

Ideias para discussão

  1. Quais dos seus “eventos” atuais são secretamente comandos, e que acoplamento vocês removeriam modelando-os com honestidade?
  2. Se vocês reproduzissem um dia inteiro de eventos pelos seus consumidores amanhã, o que quebraria, e o que isso diz sobre a idempotência e a segurança de reprodução de vocês?
  3. Quais partes do seu domínio realmente precisam da trilha de auditoria da event sourcing, e quais são estado simples que vocês só complicariam usando sourcing?
  4. Como vocês migrariam de um barramento corporativo de serviços legado ou de uma teia de integrações ponto a ponto sem uma virada arriscada em big-bang?
  5. Para o seu processo de vários serviços mais importante, é uma saga orquestrada de verdade, com compensações, ou uma coreografia emergente que nenhum lugar único descreve?

Principais conclusões

  • A arquitetura orientada a eventos compra desacoplamento, escala independente, reprodução e trilhas de auditoria e custa compreensibilidade e complexidade operacional, então escolham-na por interação onde o benefício é real.
  • Mantenham distintos os eventos (fatos que aconteceram), os comandos (pedidos para agir) e as mensagens (o envelope), porque a confusão causa erros reais de acoplamento.
  • Projetem para a entrega pelo menos uma vez e tornem idempotente todo consumidor. A entrega exatamente uma vez é um mito, e os efeitos exatamente uma vez são uma conquista de engenharia.
  • A ordenação é por partição, os esquemas são contratos que pertencem a um registro e o outbox, as filas de mensagens mortas e a contrapressão são a tubulação que torna a mensageria pronta para produção.
  • Usem sagas com compensações em vez do commit em duas fases e recorram à event sourcing e ao CQRS só onde as necessidades de auditoria e de consulta justificam seu alto custo.
  • Os fluxos assíncronos são silenciosos quando falham, então os IDs de correlação, o monitoramento da defasagem do consumidor e o rastreamento de ponta a ponta são a diferença entre um sistema operável e um invisível.

Referências e leitura complementar

  • Martin Kleppmann, Designing Data-Intensive Applications
  • Gregor Hohpe and Bobby Woolf, Enterprise Integration Patterns
  • Chris Richardson, Microservices Patterns (sagas, transactional outbox, CQRS)
  • Sam Newman, Building Microservices
  • Ben Stopford, Designing Event-Driven Systems
  • Adam Bellemare, Building Event-Driven Microservices
  • Vaughn Vernon, Implementing Domain-Driven Design (event sourcing and CQRS)
  • Martin Fowler, “Event Sourcing” and “CQRS” (martinfowler.com articles)
  • Hector Garcia-Molina and Kenneth Salem, “Sagas” (1987)