Domain Events: comunicação entre bounded contexts
1. Fundamentos dos Domain Events no DDD
1.1. Definição e propósito
Domain Events são registros imutáveis de algo relevante que aconteceu no domínio. No contexto do Domain-Driven Design (DDD), eles capturam fatos de negócio que podem interessar a outros componentes do sistema, especialmente bounded contexts vizinhos. Um evento não é uma instrução ("faça algo"), mas uma notificação ("algo aconteceu").
// Exemplo de Domain Event
{
"eventId": "e7b8c9d0-1234-5678-9abc-def012345678",
"aggregateId": "pedido-98765",
"type": "PedidoCriado",
"timestamp": "2025-04-10T14:30:00Z",
"version": 1,
"payload": {
"clienteId": "cli-4455",
"itens": 3,
"valorTotal": 299.90
}
}
1.2. Diferença entre Domain Events, Integration Events e Eventos Técnicos
É essencial distinguir os tipos de eventos em uma arquitetura:
- Domain Events: representam fatos do domínio (ex:
PagamentoConfirmado). São modelados com a linguagem ubíqua e carregam significado de negócio. - Integration Events: são Domain Events serializados e publicados para outros bounded contexts. Eles podem conter informações adicionais para viabilizar a comunicação entre sistemas.
- Eventos Técnicos: não têm relação com o domínio (ex:
ServidorReiniciado,CacheExpirou). São úteis para operações, mas não devem ser confundidos com eventos de negócio.
1.3. Características essenciais
- Imutabilidade: uma vez criado, o evento nunca é alterado.
- Nome no passado:
PedidoCriado,EstoqueReservado,FreteCalculado. O nome reflete um fato consumado. - Relevância para o negócio: o evento deve ter significado para stakeholders. Se não gera reação em outro contexto, provavelmente não é um Domain Event.
2. O Papel dos Domain Events na Comunicação entre Bounded Contexts
2.1. Desacoplamento temporal e espacial
Quando bounded contexts se comunicam via eventos, eles não precisam estar online simultaneamente. O contexto A publica um evento e segue seu fluxo; o contexto B processa quando estiver disponível. Isso reduz acoplamento e aumenta resiliência.
2.2. Eventos públicos vs. privados
- Public Events: são contratos entre bounded contexts. Exigem versionamento, documentação e governança.
- Private Events: são internos a um contexto. Podem evoluir livremente, sem impacto externo.
// Exemplo de contrato público versionado
{
"eventType": "estoque.v1.EstoqueReservado",
"schema": "https://api.exemplo.com/schemas/estoque-reservado-v1.json"
}
2.3. Consistência eventual
Eventos não garantem consistência imediata. A comunicação assíncrona implica que, por um período, os contextos podem ter visões diferentes do estado. O sistema deve ser projetado para tolerar essa latência e reconciliar dados quando necessário.
3. Modelagem e Identificação de Domain Events Significativos
3.1. Descoberta com Event Storming
Event Storming é uma técnica colaborativa onde a equipe identifica eventos do domínio em post-its. O processo começa com eventos caóticos, depois são organizados em sequência temporal e agrupados por bounded context.
3.2. Critérios para relevância
Um evento é relevante quando:
- Gera uma reação em outro contexto (ex: PedidoCriado → contexto de estoque reserva itens)
- Representa um ponto de decisão ou mudança de estado importante
- É registrado para auditoria ou rastreabilidade
3.3. Exemplos típicos
// Eventos comuns em um sistema de e-commerce
PedidoCriado
PagamentoConfirmado
EstoqueReservado
NotaFiscalEmitida
EntregaAgendada
CupomAplicado
4. Estrutura e Serialização de Domain Events
4.1. Campos obrigatórios
Todo Domain Event deve conter:
- EventId: identificador único global
- AggregateId: referência ao agregado que gerou o evento
- Timestamp: momento da ocorrência
- Version: número da versão do schema
- Payload: dados específicos do evento
4.2. Schema evolution e versionamento
Eventos evoluem. Estratégias comuns:
- Aditiva: novos campos são opcionais
- Versionamento explícito: PedidoCriadoV1, PedidoCriadoV2
- Schema Registry: ferramentas como Confluent Schema Registry validam compatibilidade
4.3. Estratégias de serialização
// JSON: simples e legível (mais comum)
{ "eventId": "...", "type": "PedidoCriado", ... }
// Avro: compacto e com schema embutido (bom para Kafka)
// Protobuf: eficiente e fortemente tipado (bom para gRPC)
5. Publicação e Entrega de Eventos entre Contextos
5.1. Mecanismos de publicação
- Message Broker (Kafka, RabbitMQ): ideal para comunicação entre bounded contexts. Kafka é preferido por sua durabilidade e replay de eventos.
- Event Bus in-memory: útil apenas dentro do mesmo processo (não atravessa contextos).
5.2. Garantias de entrega
- Pelo menos uma vez: o evento pode ser entregue mais de uma vez. Consumidores devem ser idempotentes.
- Exatamente uma vez: mais difícil de implementar, mas possível com sistemas como Kafka (transacional) e idempotência.
5.3. Dead letter queues e retry policies
Eventos que falham repetidamente devem ser enviados para uma fila de mensagens mortas (DLQ). Políticas de retry com backoff exponencial evitam sobrecarga.
// Configuração de retry
{
"maxRetries": 3,
"backoffBaseMs": 1000,
"backoffMultiplier": 2,
"dlqTopic": "eventos-falhos"
}
6. Consumo e Reação a Eventos em Contextos Distintos
6.1. Handlers de eventos
Handlers devem residir na camada de aplicação, não na de domínio. Eles orquestram a lógica de negócio em resposta ao evento recebido.
6.2. Processamento síncrono vs. assíncrono
- Síncrono: o consumidor processa imediatamente. Pode causar acoplamento temporal.
- Assíncrono: o evento é enfileirado e processado em lote. Mais resiliente, mas com maior latência.
6.3. Idempotência e deduplicação
Consumidores devem ser idempotentes: processar o mesmo evento duas vezes não deve causar efeitos colaterais. Estratégias:
- Armazenar IDs de eventos já processados
- Usar chaves únicas no banco de dados
// Exemplo de deduplicação
if (eventStore.exists(event.eventId)) {
return; // evento já processado
}
processEvent(event);
eventStore.save(event.eventId);
7. Tratamento de Erros, Sagas e Consistência Final
7.1. Sagas coreografadas vs. orquestradas
- Coreografada: cada contexto reage a eventos e publica novos eventos. Mais descentralizada, mas difícil de rastrear.
- Orquestrada: um coordenador central (saga manager) decide o fluxo. Mais controlada, mas com ponto único de falha.
7.2. Compensação de eventos
Quando um passo falha em uma saga, eventos de compensação são publicados para reverter operações anteriores.
// Exemplo de evento de compensação
{
"eventId": "...",
"type": "PagamentoEstornado",
"compensates": "PagamentoConfirmado",
"payload": { "transacaoId": "tx-12345" }
}
7.3. Monitoramento de consistência
Ferramentas como auditoria de eventos, health checks e reconciliação periódica detectam eventos perdidos.
8. Boas Práticas e Anti-Patterns na Comunicação por Eventos
8.1. Evitar acoplamento excessivo
Não compartilhe entidades inteiras. Publique apenas os dados necessários para o contexto consumidor.
// Ruim: compartilha a entidade completa
{ "cliente": { "id": 1, "nome": "João", "endereco": {...}, "telefone": "..." } }
// Bom: apenas dados relevantes
{ "clienteId": 1, "nome": "João" }
8.2. Cuidados com granularidade
Eventos muito granulares (ex: ItemAdicionadoAoCarrinho) podem gerar ruído. Eventos muito genéricos (ex: AlteracaoNoSistema) perdem significado de negócio.
8.3. Documentação e governança
Mantenha um catálogo de eventos com:
- Nome, versão, schema, contexto de origem e destino
- Exemplos de payload
- Contrato de compatibilidade
Referências
- Martin Fowler: Domain Event — Artigo seminal que define o padrão Domain Event e suas aplicações em DDD.
- Microsoft: Domain Events: Design and Implementation — Guia prático da Microsoft sobre implementação de Domain Events em microserviços com .NET.
- Confluent: Event Sourcing and Domain Events — Explicação detalhada sobre como usar Kafka para publicar e consumir Domain Events em arquiteturas orientadas a eventos.
- Vaughn Vernon: Implementing Domain-Driven Design (Capítulo 8) — Seção do livro clássico de DDD que aborda Domain Events como mecanismo de comunicação entre bounded contexts.
- Event Storming: How to Discover Domain Events — Site oficial da técnica Event Storming, com guias e exemplos para identificação colaborativa de eventos de domínio.