Quantos eventos no seu sistema são emitidos em situações onde o fato que eles representam simplesmente não aconteceu?
Um PedidoCriado disparado por uma rotina de correção. Um PagamentoConfirmado emitido por um job de reprocessamento. Um UsuarioCadastrado lançado por uma migração de base legada. O fato não aconteceu, mas o evento foi emitido assim mesmo — porque “já tem tudo plugado”.
Essa é a confusão mais destrutiva em event-driven architecture. Não é sobre nomenclatura. É sobre semântica. E quando a semântica quebra, o sistema inteiro passa a operar sobre premissas falsas.
Eventos e Comandos se parecem, mas representam coisas opostas
Um evento e um comando:
- podem ter o mesmo payload
- podem trafegar pela mesma infraestrutura
- podem até ter nomes parecidos
Mas a intenção que cada um carrega é fundamentalmente diferente.
Um evento é a notificação de um fato. Algo aconteceu. PedidoCriado significa que um pedido foi criado. Um passado consumado, irreversível.
O produtor do evento
- não sabe quem consome
- não espera uma reação específica
- não tem responsabilidade sobre o que acontece depois
Ele apenas registra que um fato ocorreu.
Um comando é um pedido de ação. Algo precisa acontecer. SepararEstoque significa que alguém está solicitando ao serviço de estoque que execute uma operação. O emissor sabe para quem está enviando, espera uma execução, e assume responsabilidade pela intenção.
O evento diz “isso aconteceu”. O comando diz “faça isso”.
Essa distinção parece óbvia quando escrita assim. Na prática, a infraestrutura conspira contra ela. O RabbitMQ, o Kafka, o SNS, todos tratam eventos e comandos como mensagens. A ferramenta nivela o que a arquitetura deveria separar. E quando tudo é “mensagem”, a distinção semântica se perde no vocabulário do dia a dia do time.
O handler de evento é o adaptador que absorve acoplamento
Entre o evento e o comando existe uma peça que raramente recebe a atenção que merece: o handler de evento.
O handler é quem traduz fato em intenção.
Ele recebe PedidoCriado e emite:
SepararEstoqueNotificarClienteGerarFatura
Cada handler consome o evento e produz o comando específico para o serviço que precisa agir.
Essa tradução não é um detalhe de implementação. É o mecanismo central que permite a EDA funcionar sem criar um emaranhado de dependências.
Sem o handler como adaptador, você tem duas alternativas ruins.
A primeira: o serviço que cria o pedido conhece e invoca diretamente todos os serviços que precisam reagir: estoque, notificação, faturamento. A pressão cognitiva sobre esse serviço cresce a cada novo consumidor.
A segunda: os serviços consumidores interpretam o evento diretamente como instrução de ação, acoplando sua lógica interna à estrutura de um evento que pertence a outro bounded context.
O handler elimina os dois problemas.
- O produtor continua ignorando quem consome.
- O consumidor recebe um comando limpo, com a interface que ele mesmo definiu.
- O handler é a fronteira onde o acoplamento entre contextos é absorvido e isolado.

Cada seta entre o handler e o serviço destino é um comando.
Cada comando
- tem contrato próprio
- versionamento próprio
- e pode ser invocado de qualquer origem
não apenas de um handler de evento.
Onde o reaproveitamento é legítimo e onde ele quebra?
Essa arquitetura tem três pontos com características de reaproveitamento completamente diferentes. Confundir esses três níveis é a origem da maioria dos problemas.
No consumo do evento, o reaproveitamento é o design.
Um único PedidoCriado pode ter dez handlers diferentes. Cada handler traduz o mesmo fato em um comando diferente para um serviço diferente.
Adicionar um novo comportamento ao sistema significa adicionar um novo handler:
- sem alterar o produtor
- sem alterar os handlers existentes
Open/closed principle aplicado na arquitetura, não apenas no código.
Na emissão do evento, o reaproveitamento é uma mentira.
O evento está amarrado ao fato que o originou. PedidoCriado só pode ser emitido quando um pedido é efetivamente criado.
Se você precisa disparar separação de estoque por outro motivo como:
- uma correção manual
- um reprocessamento
- uma migração
emitir PedidoCriado é mentir para o sistema. O pedido não foi criado. Todos os handlers vão reagir como se tivesse sido. O cliente vai receber uma notificação de pedido que ele não fez. O faturamento vai gerar um documento fiscal duplicado. A semântica contamina todo o fluxo downstream.
Nos comandos, o reaproveitamento é monumental.
SepararEstoque pode ser invocado pelo handler de PedidoCriado.
- Pode ser invocado por uma API de correção manual.
- Pode ser invocado por um job de reprocessamento.
- Pode ser invocado por um teste automatizado.
O comando não carrega contexto de origem, ele expressa uma capacidade do serviço. E capacidades são, por definição, reutilizáveis.
Quando alguém reclama que “precisou emitir o evento de novo porque era a única forma de disparar o fluxo”, o problema não é a arquitetura, o problema é que os comandos não foram desenhados como unidades independentes. O time construiu a integração inteira sobre eventos e não expôs os comandos como pontos de entrada autônomos.
O que acontece quando você emite eventos que não representam fatos?
Na teoria, a confusão entre evento e comando é um problema de modelagem. Na prática, as consequências são operacionais e caras.
Rastreabilidade falsa. Se PedidoCriado é emitido tanto pela criação real de um pedido quanto por uma rotina de correção, o log do sistema registra dois eventos semanticamente idênticos que representam situações completamente diferentes. Qualquer análise, auditoria ou debugging precisa de contexto externo para distinguir qual é qual. A observabilidade perde valor.
Efeitos colaterais incontroláveis. Cada handler que reage ao evento vai executar. Todos eles. Se você emitiu PedidoCriado só porque precisava do handler de estoque, vai ter que lidar com a notificação indevida, a fatura duplicada, e qualquer outro handler que o time tenha plugado desde a última vez que alguém olhou o fluxo completo. A quantidade de handlers cresce ao longo do tempo, e cada novo handler é um efeito colateral potencial para quem emite o evento fora de contexto.
Modelo mental corrompido. Quando o time percebe que eventos são emitidos sem que o fato correspondente tenha acontecido, a confiança no modelo se deteriora. Desenvolvedores começam a adicionar flags, campos de controle, condicionais dentro dos handlers para distinguir “evento real” de “evento forjado”. A complexidade acidental cresce até que ninguém confia na semântica de nenhum evento.
// Antipattern: forjando um evento para reaproveitar a cadeia
var eventoForjado = new PedidoCriado
{
PedidoId = pedidoExistente.Id,
// Campos preenchidos artificialmente
// para disparar separação de estoque
};
await _bus.Publish(eventoForjado);
// Todos os handlers vão reagir — inclusive os indesejados
// Correto: invocar o comando diretamente
var comando = new SepararEstoque
{
PedidoId = pedidoExistente.Id,
Itens = pedidoExistente.Itens,
Origem = OrigemSeparacao.CorrecaoManual
};
await _bus.Send(comando);
// Apenas o serviço de estoque reage
A diferença entre os dois trechos não é estilística. No primeiro, você acionou uma cadeia inteira de efeitos colaterais sem intenção. No segundo, você expressou exatamente o que precisava, para quem precisava.
A regra é simples, a disciplina é difícil
| Evento | Comando |
| Um evento registra que algo aconteceu | Um comando solicita que algo aconteça. |
| O handler de evento traduz fatos em solicitações. | Os comandos são os pontos de entrada reutilizáveis dos seus serviços. |
Quando você sente a necessidade de emitir um evento que não corresponde a um fato real, isso não é um sinal de que a arquitetura está limitada. É um sinal de que os comandos do serviço destino não estão expostos como deveriam.
O evento certo emitido pelo motivo errado causa mais dano do que o evento errado porque passa pelos validadores, é aceito pelos handlers, e contamina o sistema inteiro enquanto parece perfeitamente correto nos logs.









0 comentários