Como modelar eventos de domínio com clareza e sem ambiguidade

1. Fundamentos da modelagem de eventos de domínio

Eventos de domínio representam fatos consumados que ocorreram dentro de um contexto delimitado (bounded context). No design orientado a eventos, eles são a espinha dorsal da comunicação entre agregados e sistemas, registrando mudanças de estado que importam para o negócio.

A principal diferença entre tipos de eventos é:
- Evento de domínio: algo que aconteceu no negócio (ex: Pedido.Confirmado)
- Evento de integração: comunicação entre sistemas (ex: Pedido.EnviadoParaERP)
- Evento de sistema: mudança técnica (ex: Cache.Atualizado)

Para identificar eventos relevantes, utilize a linguagem ubíqua do domínio. Pergunte: "O que os especialistas do negócio dizem que aconteceu?" Se a frase "Quando X acontece, fazemos Y" surge naturalmente, você encontrou um evento.

2. Estrutura do nome do evento: verbo no passado + contexto

O padrão mais claro é [Entidade].[AçãoRealizada] com verbo no particípio passado:

Pedido.Faturado
Cliente.Cadastrado
Estoque.Reservado
Pagamento.Autorizado

Evite nomes genéricos como DadosAtualizados — prefira Cliente.EnderecoAlterado. Qualificadores adicionais resolvem ambiguidades:

// Ambíguo
Pedido.Modificado

// Claro
Pedido.ValorTotalRecalculado
Pedido.ItemRemovido

3. Definição de propriedades essenciais do evento

Todo evento de domínio deve conter:

Evento: Pedido.Faturado
{
  "eventId": "evt_9a8b7c6d-1234",
  "timestamp": "2025-03-15T14:30:00Z",
  "version": 1,
  "data": {
    "pedidoId": "ped_12345",
    "clienteId": "cli_67890",
    "valorTotal": 1250.00,
    "dataFaturamento": "2025-03-15"
  }
}

Regras importantes:
- Identificador único: UUID ou chave globalmente única
- Timestamp: UTC, formato ISO 8601
- Versão do evento: número inteiro para rastrear schema
- Dados do agregado: apenas o necessário para o contexto. Não inclua detalhes de implementação como IDs de tabelas ou campos técnicos.

4. Granularidade adequada: eventos atômicos vs. eventos compostos

A granularidade errada é uma das maiores fontes de ambiguidade. Use eventos atômicos quando cada mudança tem significado independente:

// Eventos atômicos (recomendado para independência)
Pedido.Item.Adicionado
Pedido.Item.Removido
Pedido.Item.QuantidadeAlterada

// Evento composto (use quando as mudanças são inseparáveis)
Pedido.PrecoRecalculado

Regras para decidir:
- Evento atômico: quando consumidores precisam reagir a mudanças específicas
- Evento composto: quando várias mudanças formam uma única transação lógica e inseparável
- Evite: eventos que misturam responsabilidades ou são tão granulares que geram ruído

Exemplo de modelagem ruim:

// Ruim: muito genérico e ambíguo
Cliente.Alterado

// Ruim: muito granular sem necessidade
Cliente.PrimeiroNomeAlterado
Cliente.UltimoNomeAlterado
Cliente.EmailAlterado

// Bom: agrupamento lógico
Cliente.DadosCadastraisAlterados
Cliente.EmailAlterado

5. Versionamento e evolução de eventos

Eventos evoluem. Use versionamento semântico para garantir compatibilidade:

// Versão 1
Evento: Pedido.Faturado (v1)
{
  "pedidoId": "ped_123",
  "valorTotal": 1250.00
}

// Versão 2 (adiciona campo opcional)
Evento: Pedido.Faturado (v2)
{
  "pedidoId": "ped_123",
  "valorTotal": 1250.00,
  "descontoAplicado": 50.00  // campo opcional
}

Estratégias de migração:

// Consumidor v1 ignora campos desconhecidos
// Consumidor v2 usa campo com default
{
  "pedidoId": "ped_123",
  "valorTotal": 1250.00,
  "descontoAplicado": null  // interpretado como 0
}

Nunca remova campos existentes sem depreciação. Adicione campos como opcionais e documente a transição.

6. Documentação e rastreabilidade dos eventos

Crie um catálogo centralizado de eventos. Exemplo de entrada:

Evento: Pedido.Faturado
Versão: 2
Origem: Serviço de Faturamento
Destino: Serviço de Contabilidade, Serviço de Notificação
Descrição: Disparado quando um pedido é faturado com sucesso
Schema: pedido_faturado_v2.avsc

Exemplo:
{
  "eventId": "evt_abc123",
  "timestamp": "2025-03-15T14:30:00Z",
  "version": 2,
  "data": {
    "pedidoId": "ped_12345",
    "clienteId": "cli_67890",
    "valorTotal": 1250.00,
    "descontoAplicado": 50.00
  }
}

Use schemas para validação automatizada. JSON Schema é acessível para todos:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "eventId": { "type": "string", "pattern": "^evt_" },
    "timestamp": { "type": "string", "format": "date-time" },
    "version": { "type": "integer", "minimum": 1 },
    "data": {
      "type": "object",
      "properties": {
        "pedidoId": { "type": "string" },
        "valorTotal": { "type": "number", "minimum": 0 }
      },
      "required": ["pedidoId", "valorTotal"]
    }
  },
  "required": ["eventId", "timestamp", "version", "data"]
}

7. Padrões para evitar ambiguidades em cenários complexos

Tratamento de valores nulos vs. ausência

// Campo ausente: consumidor assume que não mudou
{
  "descontoAplicado": null  // campo presente, mas nulo = desconto zerado
}

// Campo ausente: consumidor usa valor do evento anterior
{
  // "descontoAplicado" não está presente
}

Eventos de compensação

Use nomenclatura clara para ações de rollback:

Pagamento.Autorizado
Pagamento.Estornado      // compensação de Autorizado
Estoque.Reservado
Estoque.ReservaLiberada  // compensação de Reservado

Idempotência e eventos duplicados

Todo consumidor deve tratar eventos duplicados. Use o eventId para deduplicação:

// Estratégia: armazenar eventIds processados
// Se eventId já foi processado, ignorar
// Se não, processar e armazenar eventId

// Exemplo de evento duplicado seguro
Evento: Pedido.Faturado
eventId: "evt_9a8b7c6d-1234"  // mesmo ID = mesma ação

Eventos simultâneos

Quando eventos podem ocorrer ao mesmo tempo, use timestamps e ordenação lógica:

// Dois eventos no mesmo milissegundo
// Use sequenciador lógico para ordenar
Evento: Pagamento.Autorizado (timestamp: 2025-03-15T14:30:00.000Z, seq: 1)
Evento: Pedido.Confirmado   (timestamp: 2025-03-15T14:30:00.000Z, seq: 2)

Conclusão

Modelar eventos de domínio com clareza exige disciplina em nomenclatura, granularidade, versionamento e documentação. Cada evento deve ser um fato consumado, imutável e auto-contido. Use a linguagem ubíqua como guia, evite ambiguidades com qualificadores precisos e mantenha um catálogo centralizado. Com essas práticas, seus eventos se tornam a fonte de verdade confiável para todo o ecossistema orientado a eventos.

Referências