Precisamos conversar sobre Arquitetura Orientada a Eventos

Published on August 7, 2025

"Emitir evento e sair andando" não significa estar desacoplado de verdade.

Um dos grandes atrativos da arquitetura orientada a eventos (EDA) é o tal desacoplamento. E sim, ela oferece uma liberdade muito maior do que os modelos tradicionais de requisição e resposta.

No modelo tradicional, se o serviço A precisa do serviço B, ele faz uma chamada direta:

“Oi B, preciso que você aprove esse pagamento AGORA.”

E aí A fica esperando uma resposta de B. Se B estiver fora do ar, travado ou lento, A também falha. Esse é o famoso acoplamento temporal: se um não responde, o outro não segue. É um relacionamento dependente, quase um namoro codependente entre serviços. 😅

Inscreva-se gratuitamente para receber novas postagens e apoiar meu trabalho.

EDA promete o quê?

Na EDA, o serviço A simplesmente emite um evento, como por exemplo:

Evento: PagamentoAprovado

E segue sua vida.

Ele não sabe:

  • Quem vai escutar esse evento

  • O que cada um vai fazer com ele

  • Quando vão reagir (se é que vão)

Isso parece lindo:

“Olha só, o A não precisa mais conhecer ninguém. Tá tudo desacoplado!”

Mas… será mesmo?

Tecnicamente, sim.
Esse é o que chamamos de desacoplamento técnico:

  • A não conhece B

  • Não há chamadas diretas

  • Não há tempo de resposta exigido

  • Os serviços podem escalar separadamente

Mas do ponto de vista de negócio, o buraco é mais embaixo.

O desacoplamento lógico é outra conversa

Vamos imaginar o seguinte cenário prático:

  1. O Serviço A aprova o pagamento de um pedido.

  2. Ele publica um evento PagamentoAprovado.

  3. A lógica de negócio diz que, após o pagamento aprovado, o pedido deve ser faturado automaticamente.

Quem fatura o pedido?
Um Serviço B, que escuta o evento e executa essa tarefa.

Agora pense:

  • Se ninguém estiver ouvindo o evento...

  • Se o Serviço B estiver fora do ar...

  • Se o handler quebrar silenciosamente...

O pedido nunca será faturado e o Serviço A nunca saberá que algo deu errado.

Esse é o ponto: o desacoplamento lógico não aconteceu

Mesmo sem chamada direta, o Serviço A depende do Serviço B para completar o fluxo de negócio. É como se A dissesse:

“Eu não te conheço, mas tô contando que você vai fazer sua parte.”

Ou seja:

  • A execução está desacoplada

  • Mas a responsabilidade do negócio continua interligada

Desacoplamento lógico significa que o sucesso do negócio não depende da reação de outro serviço. E isso, na prática, é muito mais difícil de alcançar do que parece.

Uma analogia simples

Imagine uma cafeteria. O atendente (Serviço A) coloca um pedido no balcão com um aviso:

“Pedido pronto para entrega.”

Ele não fala com o entregador (Serviço B), só deixa ali.

Se o entregador não pegar o pedido:

  • O cliente nunca recebe.

  • O atendente nem sabe que o pedido ficou lá.

Tudo funcionou "tecnicamente", mas a experiência do cliente quebrou.
Esse é o risco de confiar demais no “emitir e esquecer”.

A ilusão do 100% desacoplado

Muitos arquitetos caem nessa armadilha:
confundem baixo acoplamento técnico com independência real de negócio.

Mas a verdade é que um serviço pode não conhecer nenhum outro, e ainda assim depender deles para cumprir o que prometeu.

O Acoplamento Semântico Disfarçado

Um dos erros mais sutis e perigosos em arquiteturas orientadas a eventos é o acoplamento semântico. Ele não aparece em gráficos nem em chamadas diretas, mas está ali, escondido no que cada serviço “espera” que um evento signifique.

Vamos a um exemplo bem comum: um evento chamado ClienteAtualizado.

Na teoria, parece ótimo:

“Sempre que um cliente for atualizado, todos os serviços interessados ficam sabendo.”

Mas, na prática, cada serviço interpreta esse evento de um jeito diferente:

  • O Serviço de Marketing usa esse evento para atualizar listas de e-mail. Ele assume que o campo email sempre estará presente.

  • O Serviço de Entregas precisa do campo endereco.cidade para atualizar a rota de envio e espera que ele nunca venha nulo.

  • O Serviço de Compliance só considera a atualização válida se o CPF já tiver sido verificado e presume que isso já aconteceu toda vez que o evento é emitido.

Esses serviços não conversam diretamente entre si. Cada um escuta o evento e age com base no que acredita que ele significa.

A mudança que quebra tudo (sem ninguém perceber)

Agora imagine que o time responsável pelo evento ClienteAtualizado muda uma regra interna:

Antes, o evento era emitido somente quando informações críticas eram alteradas como endereço ou CPF. Agora, ele passa a ser emitido sempre que qualquer campo mudar, até mesmo algo irrelevante como o número do telefone.

E o que acontece?

  • O Serviço de Marketing começa a atualizar e-mails mesmo quando eles nem mudaram ou pior, sobrescreve com dados antigos.

  • O Serviço de Entregas quebra porque o campo cidade agora vem vazio (afinal, o endereço não mudou só o telefone).

  • O Serviço de Compliance interpreta que o cliente passou por verificação de dados, mas isso não é mais verdade.

O evento foi emitido. Os consumidores o processaram. Nenhum erro de rede, nenhuma exceção visível. Mas... o sistema agora está logicamente inconsistente.

O que é acoplamento semântico, afinal?

É quando serviços não estão conectados diretamente, mas ainda dependem do significado do que o outro envia.

Você acha que está desacoplado, porque ninguém chama ninguém diretamente.
Mas na verdade, todo mundo está acoplado ao “intuito” daquele evento.

E se esse significado muda, tudo quebra silenciosamente.

O efeito dominó dos eventos centrais

Eventos “famosos” como PedidoCriado, PagamentoRecebido ou ClienteAtualizado geralmente são consumidos por vários serviços.

E aí mora mais um problema:
qualquer pequena mudança nesse evento pode causar um estrago em cadeia.

  • Renomeia um campo? Alguém quebra.

  • Muda o formato de uma data? Outro interpreta errado.

  • Adiciona um campo obrigatório sem valor default? Dá exceção no deserializador.

E o pior: você nem sempre sabe quem consome esse evento.
A EDA não exige que isso seja documentado. Parece liberdade, mas vira dívida técnica invisível.

Um bilhete na praça

Pense nos eventos como bilhetes deixados em um mural público.

Você escreve algo, deixa lá, e segue seu caminho.
Você não sabe quem vai ler… mas o que você escreveu pode mudar o que essas pessoas farão no dia delas.

Por isso, o fato de você não saber quem está ouvindo, não te isenta da responsabilidade sobre o que está sendo dito.

E é justamente aqui que começam os problemas com eventos mal projetados.

Eventos Anêmicos e o Desespero do Payload Genérico

“event_ocorreu” com 80 campos no JSON? Isso não é evento, é uma gambiarra síncrona disfarçada.”

Vamos imaginar uma situação real do dia a dia: você está numa empresa grande e recebe um e-mail com o seguinte título:

Assunto: "Ação realizada com sucesso"

Aí você abre o e-mail, e o corpo diz:

"Foi feito. Veja os detalhes abaixo."
...e vem um anexo com 80 páginas.

Você não sabe quem fez, o que fez, por que fez, quando fez, nem o que você deveria fazer com essa informação. Só sabe que “algo aconteceu”. Agora é você quem precisa escanear todo aquele PDF, tentar entender o contexto, identificar se precisa reagir ou se pode ignorar.

Esse e-mail é o equivalente a muitos eventos emitidos por aí.

Eventos anêmicos: o que são?

Eventos anêmicos são aqueles que não carregam semântica clara.
São genéricos, ambíguos, e jogam a responsabilidade de interpretação para quem consome.

Você já viu algo assim?

{
  "event": "event_ocorreu",
  "payload": {
    "id": 123,
    "nome": "João",
    "email": "[email protected]",
    "cpf": "123.456.789-00",
    "dataCriacao": "2023-05-01",
    "ultimaAtualizacao": "2025-08-06",
    ...
    // outros 70 campos
  }
}

Sem eventName, sem contexto, sem motivo aparente. Um event_ocorreu que tanto pode ser uma criação, uma atualização ou uma remoção. E o consumidor? Que se vire.

Qual o problema disso?

1. Você força o consumidor a ser mágico

Ele precisa “adivinhar” o que está acontecendo.
Precisa comparar valores atuais com anteriores, ou aplicar lógica de negócio para deduzir que foi uma atualização e não uma criação.

O evento não tem intenção. Ele é só um pacote de dados.
E evento sem intenção não é evento é só ruído.

2. Você perde clareza e aumenta a complexidade

Quanto maior e mais genérico o payload, mais difícil fica para outros serviços entenderem o que realmente mudou, e se devem ou não reagir.

Serviços começam a colocar lógica do tipo:

if (payload.email !== previousEmail) {
    // então isso é uma atualização importante
}

Ou pior:

if (payload.status === '2' && payload.tipo === 'A') {
    // então é um evento válido para mim
}

Isso cria acoplamento por inferência. Cada serviço precisa interpretar os dados com base em suposições internas, o que explode a complexidade e dificulta a manutenção.

3. Você expõe dados desnecessários ou sensíveis

Num payload de 80 campos, a chance de expor informação confidencial ou irrelevante é alta. Alguns consumidores nem deveriam ter acesso a certos campos (como CPF ou dados financeiros), mas como o evento é “genérico”, todo mundo recebe tudo.

Isso quebra princípios de segurança, privacidade e isolamento de domínios.

Eventos ricos em intenção, enxutos em forma

Um bom evento é semântico, explícito e focado.
Ele diz claramente o que aconteceu, por que aconteceu e qual foi o contexto da ação.

Por exemplo:

{
  "eventName": "ClienteAtualizado",
  "context": "PortalCliente",
  "statusReason": "AtualizacaoDeEndereco",
  "payload": {
    "clienteId": 123,
    "cpf": "2200088818"
    "novoEndereco": {
      "cidade": "Belo Horizonte",
      "uf": "MG"
    }
  },
  "timestamp": "2025-08-06T14:32:00Z"
}

Esse evento comunica:

  • O que aconteceu: Um cliente foi atualizado.

  • Onde aconteceu: Pelo Portal do Cliente.

  • Por que aconteceu: Por causa de uma atualização de endereço.

  • Quais dados são relevantes: Somente o endereço, e não o CPF, e-mail, RG ou qualquer outra coisa irrelevante.

Por que eventName, context e statusReason são importantes?

  • eventName: define a ação semântica. Torna o evento identificável e documentável.

  • context: informa onde ou em qual fluxo esse evento foi gerado. Isso ajuda outros serviços a entenderem se devem reagir ou ignorar.

  • statusReason: explica a motivação da emissão. Evita ambiguidades, dá rastreabilidade e melhora observabilidade.

Com esses campos, você transforma um pacote opaco de dados em uma mensagem clara de intenção.

Produtividade e manutenibilidade agradecem

Ao criar eventos com semântica clara, seus consumidores:

  • Não precisam interpretar dados — só reagir a ações claras.

  • Conseguem evoluir de forma independente, já que entendem o que o evento significa sem depender de lógica oculta.

  • Reduzem o risco de bugs silenciosos causados por inferências erradas.

  • Têm uma melhor observabilidade — porque logs, dashboards e alertas podem ser construídos com base em eventName e statusReason, e não em campos genéricos.

E o contrário? Um “evento” com todos os dados da entidade?

Isso é só uma API disfarçada de evento.
Você está emitindo um dump1 da entidade esperando que outros sistemas montem lógica em cima dela. Ou seja, você não desacoplou nada e apenas mudou o lugar onde o acoplamento acontece.

✅ Resumo:

  • Evento bom tem nome, tem motivo, tem foco.

  • Evite payloads gigantescos e genéricos.

  • Prefira eventos ricos em intenção, mesmo que enxutos em forma.

  • Campos como eventName, context e statusReason não são burocracia, são investimentos em clareza, rastreabilidade e autonomia entre serviços.

Actions vs Events: Quem faz o quê nessa história?

Em uma arquitetura orientada a eventos, falamos muito sobre eventos, o que aconteceu, quem emitiu, quem escutou, e o que fazer com isso. Mas pouco se fala sobre as ações que originam esses eventos. E isso pode causar confusão conceitual e técnica.

Vamos esclarecer:
🔁 Toda ação pode gerar um evento.
📣 Mas nem todo evento representa diretamente uma ação.

O que é uma Action?

Uma Action (ou Ação) é uma intenção ativa de mudança. É o "comando" que parte de um agente (humano ou sistema) para provocar uma reação.

Exemplos:

  • O cliente clicou em "Atualizar Endereço"

  • Um operador aprovou um pagamento manualmente

  • Um cron job do sistema iniciou o processo de conciliação

Essa ação não é observável diretamente pelo restante do sistema — ela dispara uma cadeia de eventos, que sim, serão percebidos e reagidos.

E o que é o Evento, então?

O Evento é o registro do que realmente aconteceu como consequência de uma ação (ou de outra cadeia de eventos).

Continuando os exemplos:

  • EnderecoAtualizado é o evento resultante da ação "Atualizar Endereço"

  • PagamentoAprovado é o evento disparado após a ação de aprovação

  • ConciliaçãoIniciada pode ser um evento emitido quando o job do sistema começa a rodar

O evento é uma mensagem de fato. Algo ocorreu. E outros serviços podem reagir a isso.

Então: A ação dispara o evento — e não o contrário.

Isso é importante. Eventos não devem ser confundidos com comandos.

Ação = "Quero que algo aconteça"
Evento = "Isso aconteceu"

E se quisermos salvar um histórico? Vale salvar as Actions?

Sim e pode ser extremamente útil para rastreabilidade, auditoria e analytics.
Persistir ações pode trazer clareza sobre intenção do usuário, frequência de operações, fluxos iniciados, erros de tentativa, etc.

Por exemplo, imagine uma tabela ActionHistory:

Essa tabela armazena ações tomadas por alguém (ou algo). Ela é diferente da tabela de eventos, que pode armazenar:

Mas por que salvar as Actions além dos Eventos?

  • A ação carrega a intenção, mesmo que falhe.

  • O evento carrega a consequência, caso tenha sucesso.

  • Juntas, elas contam a história completa.

Exemplo:

  1. Ação: Cliente tenta atualizar o endereço →

  2. Evento: EnderecoAtualizado emitido com sucesso

Ou:

  1. Ação: Tentativa de atualizar o endereço →

  2. Evento: Nenhum emitido (porque falhou na validação)

Você só vai saber que a tentativa existiu se registrar a action também.

O que persistir em uma tabela de histórico de Actions?

  • actionId: identificador único

  • actionType: nome da ação (ex: "AtualizarEndereco")

  • timestamp: quando foi feita

  • userId ou source: quem executou

  • context: de onde veio (ex: Portal, API externa, cron job)

  • status: sucesso, falha, pendente

  • requestPayload: os dados enviados

  • errorMessage (opcional): se falhou, por quê?

Dê nome aos bois 😅

  • Ação é intenção,

  • Evento é consequência,

  • Uma arquitetura orientada a eventos fica mais rica e mais auditável quando a gente entende essa separação,

  • E sim, persistir ações é saudável, especialmente em sistemas críticos, financeiros, operacionais ou regulados,

É legal e importante a gente entender todos esses assuntos, talvez o diagrama abaixo possa ajudar também:

Handler: o profissional que atende a chamada do evento

Agora que você já entendeu a diferença entre ação (a intenção) e evento (o que aconteceu), está na hora de conhecer quem realmente faz algo com o evento: o handler.

Pense no evento como o telefone tocando.
O handler? É quem atende e faz algo útil com a ligação.

O que é um Handler, afinal?

Em uma arquitetura orientada a eventos, um handler é a unidade de lógica responsável por responder a um evento específico.
Quando um serviço escuta um tópico ou canal de eventos (ex: Kafka, RabbitMQ, SNS), ele precisa saber o que fazer quando um determinado evento chegar.
Essa responsabilidade fica centralizada em uma função ou classe chamada handler.

Ou seja:

  • O evento carrega a informação: "algo aconteceu".

  • O handler recebe esse evento e decide: "o que faço com isso?"

Onde ele aparece no código?

No mundo real, você pode encontrar um handler como:

class PedidoCriadoHandler {
  async handle(event: PedidoCriadoEvent) {
    // lógica para atualizar estoque, enviar e-mail, etc.
  }
}

Ou como uma função registrada em um consumidor Kafka, por exemplo:

kafkaConsumer.on('PedidoCriado', async (event) => {
  // esse bloco é o handler na prática
});

O ponto-chave é: o handler contém a lógica de reação a um evento específico.

Onde os programadores se confundem?

  1. Handler não é service

    • Muitos confundem handler com service.

    • O handler usa um service, mas não é o lugar de ter regras de negócio profundas.

    • Ele deve delegar a lógica pesada para camadas apropriadas (ex: application service, use case, domain).

  2. Handler não é o publisher

    • Alguns colocam lógica de publicação de eventos dentro de um handler — e isso pode ser perigoso se não estiver claro qual evento é resposta de qual ação.

    • Um handler deveria reagir a um evento, e se emitir outro evento, isso deve ser bem justificado e claro no design.

  3. Handler não é repositório de lógica genérica

    • Colocar múltiplos eventos diferentes em um único handler é uma receita para o caos.

    • O ideal é um handler por tipo de evento, com responsabilidades únicas e bem definidas.

Papel do Handler na arquitetura orientada a eventos

O handler é o elo entre o evento passivo e a reação ativa.

Ele deve:

  • Ser especializado em um único evento

  • Ser fácil de testar isoladamente

  • Ser transparente: dado o evento, a reação deve ser clara

  • Delegar as decisões de negócio para as camadas corretas (use case, domain)

Ele não deve:

  • Tomar decisões fora do seu escopo

  • Acessar dados aleatórios para “complementar” o evento

  • Ser um lugar onde se faz tudo que sobrou

Uma analogia rápida

Imagine um botão de alarme de incêndio.
Quando alguém aperta (ação), isso dispara um alarme (evento).
Quem escuta o alarme? Os bombeiros.

O handler é o bombeiro de plantão. Ele não decide se houve incêndio nem emite o alarme, ele reage ao alarme e executa a ação necessária, como evacuar o prédio ou ligar os sprinklers2.

Se o bombeiro tiver que analisar 40 tipos de alarmes diferentes, sem saber qual é qual, a resposta vai ser confusa e atrasada. Cada alarme precisa de um handler preparado e treinado para aquele tipo específico de ocorrência.

✅ O que você precisa lembrar sobre handlers

  • Um handler é quem reage a um evento e decide o que fazer com ele.

  • Ele deve ser focado, claro, testável e isolado.

  • Não misture papéis: handler não é service, controller ou publisher.

  • Ele é o ponto de entrada para a reação ao evento, e deve ser simples, explícito e bem nomeado.

Claro! Vamos continuar com esse próximo tópico do artigo, agora abordando os perigos do excesso de eventos em uma arquitetura orientada a eventos. O estilo segue o mesmo: técnico, com analogia clara, conversado e provocativo.

Quando Tudo é Evento, Nada é Importante 🧨

Nem todo dado que muda é um evento digno de publicação.

Se cada coceira no sistema vira um “evento”, em pouco tempo você está gerenciando uma arquitetura que parece um organismo com hiperatividade sensorial: sente tudo, reage a tudo, mas não sabe o que é realmente importante.

E isso, meu caro leitor, não é arquitetura — é caos com nome bonito.

O problema do excesso de granularidade

Vamos imaginar uma entidade Cliente com os seguintes campos:

  • nome

  • telefone

  • e-mail

  • cidade

  • data de nascimento

Agora imagine que, a cada alteração em qualquer campo, seu sistema emite um evento específico:

  • ClienteNomeAlterado

  • ClienteTelefoneAlterado

  • ClienteEmailAlterado

  • ClienteCidadeAlterada

  • ClienteDataNascimentoAlterada

E sim, todos esses eventos vão parar no mesmo tópico Kafka.
E todos os serviços que escutam esses eventos... precisam decidir o que fazer com eles.

Resultado:

  • Serviços reagem a mudanças que não são relevantes para eles

  • Handlers precisam de lógica condicional para ignorar o que não importa

  • A fila fica poluída com ruído

  • O custo de observabilidade aumenta

  • A performance pode cair por causa de mensagens inúteis

Você cria uma avalanche de eventos irrelevantes, e no meio dela, o que é realmente importante passa despercebido.

Analogia: um sistema nervoso sem cérebro

Imagine um corpo humano que sente tudo com extrema sensibilidade — uma coceira, um vento, um fio de cabelo encostando — e reage fortemente a cada estímulo.

Agora tire o cérebro da equação.
O corpo reage a tudo, mas sem contexto, sem discernimento, sem prioridade.

Esse é o risco de um sistema com eventos demais e sem curadoria:
Você transforma sua arquitetura em um sistema nervoso sem cérebro.

Ou seja, você sente, dispara e processa… mas não pensa.

Sinais de que seu sistema está “reativo demais”

  • Handlers que fazem if para ignorar 90% dos eventos

  • Logs e dashboards cheios de eventos de “telefone alterado” ou “cor do botão clicado”

  • Services com alertas falsos por causa de eventos irrelevantes

  • Testes quebrando porque qualquer mudança de campo gera uma tempestade de notificações

O que fazer então?

  1. Suba o nível de semântica

    • Em vez de eventos “microscópicos” como TelefoneAlterado, pense em eventos que carregam intenção de negócio:

      • ClientePreferenciasAtualizadas

      • ContatoPrincipalModificado

  2. Agrupe eventos contextualmente

    • Agrupe alterações que fazem sentido em conjunto em eventos compostos e significativos.

  3. Tenha critérios claros de publicação

    • Nem toda mudança de estado justifica um evento.

    • Tenha uma política: “Esse evento é útil para algum consumidor? Ele representa uma decisão de negócio?”

  4. Evite "eventos de auditoria disfarçados"

    • Se o objetivo é rastrear mudanças, use logs de auditoria, não eventos em tempo real.

Publicar eventos demais sem objetivo e necessidade, não te dá mais controle, te dá mais confusão. Na dúvida, pense assim:

  • O evento comunica algo que importa para outros serviços?

  • Ele representa uma intenção de negócio, ou é só um espasmo do sistema?

Lembre-se caro leitor: eventos são sinais de significado, não notificações de qualquer alteração. Se tudo é evento, nada mais é prioritário e aí sua arquitetura não está sendo orientada a eventos, está sendo soterrada por eles.

Deixe um comentário

Reprocessar Eventos Não é Tão Simples Quanto Parece

Reemitir um evento antigo pode ser um desastre se o sistema não for idempotente.

A ideia de "reemitir eventos antigos para corrigir dados" pode soar simples na teoria:

“É só dar replay nos eventos e deixar os serviços reconciliarem o estado.”

Mas em uma arquitetura orientada a eventos, essa operação exige maturidade.
Se não houver idempotência, controle de duplicidade e versionamento consciente, esse replay pode causar duplicações de efeitos colaterais, inconsistências silenciosas e falhas difíceis de rastrear.

Por que reprocessar eventos é tão delicado?

Imagine que um serviço recebe novamente um evento de PagamentoConfirmado.
Se ele não for idempotente, ele pode:

  • Gerar uma nova ordem de entrega

  • Reenviar e-mails de confirmação

  • Deduzir estoque novamente

  • Atualizar status já processado

Reprocessar sem cuidado transforma uma arquitetura desacoplada em uma fonte de efeitos colaterais imprevisíveis — e o pior: muitas vezes silenciosos.

O que é idempotência e por que você precisa dela

Idempotência significa:

“Executar a mesma operação mais de uma vez não muda o resultado final.”

No mundo de eventos:

  • Um evento processado duas vezes não deve gerar efeitos colaterais duplicados

  • Isso vale especialmente para ações como pagamentos, notificações, atualizações de status

Como garantir idempotência (sem acoplar no handler)

A lógica de idempotência não deve morar no handler.
Ela deve ser responsabilidade de um componente reutilizável — como um serviço de orquestração, um decorator, ou até infraestrutura de mensageria que controle:

  • Se o eventId ou idempotencyKey já foi processado

  • Qual foi o resultado da última execução

  • Se deve ignorar, repetir ou compensar

O handler só deve fazer seu trabalho se o evento for considerado "novo ou válido" para ser processado.

E quanto ao versionamento de eventos?

Outra armadilha comum no replay de eventos é processar versões antigas de forma cega.

Imagine que um handler moderno espera campos obrigatórios que não existiam na versão 1 do evento. Se ele receber esse evento e tentar tratá-lo como versão atual, você pode quebrar o sistema.

Por isso, todo evento deveria carregar um campo version, e isso não deve ser tratado no handler cruamente, mas em serviços que sabem como interpretar ou traduzir cada versão.

Práticas que podem ajudar:

Garanta idempotência com um serviço externo
→ Use tabelas de ProcessedEvents com eventId para evitar duplicidades

Controle versionamento com responsabilidade clara
→ Não jogue o if (version === x) no handler —
→ Crie um EventVersionRouter ou uma entidade de domínio que saiba se adaptar à versão

Tenha um mecanismo de replay testável
→ Evite replay direto em produção sem simulação
→ Garanta que múltiplos handlers possam consumir replays sem efeito colateral

Inclua eventId, idempotencyKey e version em todos os eventos

🚨 Replay sem planejamento pode virar um monstro

O que parece uma simples reemissão de evento pode:

  • Disparar efeitos em cadeia que não deveriam ocorrer de novo

  • Criar inconsistências entre serviços que processam eventos de maneiras diferentes

  • Gerar comportamentos inesperados em sistemas que evoluíram semanticamente

Reprocessar eventos exige mais engenharia do que impulso.
É necessário projetar a arquitetura desde o início com idempotência e versionamento em mente, caso contrário, cada replay vira uma roleta russa.

O handler deve delegar.
Quem garante que o evento é válido, processável, atual ou duplicado...
é a infraestrutura, os serviços auxiliares e os contratos bem definidos.

Se não for assim, seu sistema orientado a eventos vira orientado ao pânico.

Inscreva-se gratuitamente para receber novas postagens e apoiar meu trabalho.

Eventos Sem Dono e a Governança Esquecida

Quem define a estrutura? Quem versiona? Quem garante que isso não vai mudar amanhã?

Eventos são como correspondências no correio de um sistema distribuído: alguém escreve, alguém recebe.
Mas e se ninguém souber quem escreveu, o que estava dentro, qual era o formato esperado ou por que a carta foi enviada?

Sem governança, eventos deixam de ser fontes confiáveis de informação e viram mensagens enigmáticas voando no escuro.

Quando eventos viram terra de ninguém

Arquiteturas orientadas a eventos costumam nascer com entusiasmo:

"Vamos publicar tudo! É assíncrono, escalável, bonito!"

Mas à medida que o sistema cresce, uma pergunta começa a assombrar o time:

  • Quem é responsável por esse evento aqui?

  • Posso alterar esse campo?

  • Alguém ainda consome isso?

  • Tem outro serviço emitindo o mesmo evento com estrutura diferente?

E aí você percebe:
não há contratos claros. Não há dono. Não há visibilidade.

❌ O que acontece quando não há governança

  • Times diferentes publicam eventos com o mesmo nome, mas com estrutura e semântica diferentes

  • Consumidores quebram silenciosamente porque um campo sumiu ou mudou de tipo

  • Eventos são alterados sem versionamento, causando inconsistência entre ambientes

  • Ninguém sabe se pode remover ou descontinuar um evento

  • Eventos viram APIs internas disfarçadas, com dados superdimensionados e sem contexto semântico

Eventos não são “payloads genéricos” — são contratos de domínio

Se você não trata eventos como contratos formais, você está criando o mesmo problema que APIs sem OpenAPI, sem versionamento e sem documentação causam mas agora em tempo assíncrono e distribuído, o que torna tudo pior.

🗂️ Governança exige três pilares fundamentais:

1. Event Schema

Todo evento precisa de um schema claro e versionado, com:

  • Campos obrigatórios e opcionais

  • Tipos de dados

  • Descrições de campos

  • Valor semântico de cada parte do payload

Pode ser feito com:

  • JSON Schema

  • Avro (muito usado com Kafka)

  • Protobuf

  • Ou até mesmo TypeScript com validação (para sistemas mais simples)

Sem schema, cada consumidor “acha” o que está chegando. E isso quase sempre dá errado.

2. Event Catalog

Você precisa de um catálogo público de eventos, como uma espécie de “Swagger dos eventos”:

  • Quais eventos existem

  • Quem os publica

  • Quem consome

  • Qual é a versão atual

  • Exemplo de payload

  • Justificativa de negócio

Ferramentas como Backstage, AsyncAPI, ou até Notion/Wiki bem organizado, ajudam muito aqui.

Sem catálogo, cada serviço vira uma caixa-preta com eventos voando pra todo lado.

3. Contrato de Responsabilidade

Cada evento deve ter um dono explícito:

  • Quem decide quando um novo campo pode ser adicionado?

  • Quem versiona o contrato?

  • Quem valida mudanças com os consumidores?

Sem esse “Dono do evento”, tudo é alterável por qualquer time, a qualquer momento e é assim que nascem os bugs que só explodem 3 semanas depois em produção.

A tentação de transformar eventos em APIs internas

Muitos times caem nessa armadilha:

  • Pegam uma entidade (Cliente, Pedido, Pagamento)

  • Serializam tudo em um JSON gigante

  • Publicam um evento chamado EntidadeAtualizada

  • Esperam que os consumidores “se virem” com os dados

Isso é acoplamento disfarçado.
Você está expondo o estado interno do seu sistema como se fosse uma API, sem versionamento, sem controle, sem semântica.

Eventos devem carregar fatos relevantes, não entidades inteiras.
Eles devem comunicar “o que aconteceu”, não despejar um banco de dados no tópico Kafka.

Arquitetura orientada a eventos não é só publicar mensagens — é criar uma rede de contratos confiáveis.

Sem governança:

  • Os eventos perdem valor

  • Os consumidores vivem no escuro

  • Os sistemas se tornam frágeis e imprevisíveis

Com governança:

  • Eventos viram fontes confiáveis de verdade

  • Mudanças são seguras e versionadas

  • A comunicação entre serviços é clara, observável e rastreável

Se você não sabe quem manda nos seus eventos, alguém já está sofrendo com isso… você só ainda não percebeu.

Nem Tudo Que É Evento, É Arquitetura

Event-Driven Architecture parece simples à primeira vista.
Você emite um evento, alguém consome, tudo se resolve.

Mas como vimos neste artigo, por trás da beleza do desacoplamento, existe uma série de decisões arquiteturais que não podem ser ignoradas:

  • Nem todo evento é importante — e muito menos urgente.

  • Ações e eventos são diferentes — e confundí-los afeta o design do sistema.

  • Sem idempotência e versionamento, o reprocessamento vira uma armadilha.

  • Handlers não são lugares para lógica desorganizada.

  • E principalmente: eventos precisam de dono, contrato, e responsabilidade.

EDA não é sobre “emitir e esquecer”.
É sobre projetar sistemas que saibam ouvir, reagir e evoluir com clareza e confiança.

1

Um dump, nesse caso, é uma cópia bruta e completa de um objeto ou entidade, jogada em forma de evento, sem curadoria ou intenção clara.

Ou seja, ao invés de emitir um evento com um propósito específico e bem definido (ex: EmailDoClienteAtualizado), você apenas joga um JSONzão no tópico com tudo que existe no banco sobre o cliente.


2

Conhecidos como chuveiros automáticos, são dispositivos de combate a incêndio que fazem parte de um sistema de proteção contra incêndios.