DDD: definindo bounded contexts corretamente

1. Fundamentos do Bounded Context no DDD

O Bounded Context é o conceito central do Domain-Driven Design que estabelece limites explícitos onde um modelo de domínio específico é válido e consistente. Dentro de cada contexto, a linguagem, as regras de negócio e as entidades têm significados precisos e não ambíguos.

Domínio, Subdomínio e Bounded Context não são sinônimos. O domínio é o problema central que o software resolve. Subdomínios são partes desse problema (core, supporting, generic). O Bounded Context é a solução técnica e organizacional que implementa um modelo para um subdomínio específico.

O Ubiquitous Language é a âncora que mantém o contexto coeso. Quando a equipe de negócio usa termos diferentes para a mesma ideia, ou o mesmo termo com significados diferentes, estamos diante de contextos distintos.

Exemplo: em um sistema bancário, "Conta" no contexto de Abertura de Conta significa "documentação e aprovação de crédito", enquanto no contexto de Transações significa "saldo disponível e extrato". São modelos diferentes para a mesma palavra.

// Contexto: Abertura de Conta
Classe Conta {
    status: Pendente | Aprovada | Rejeitada
    documentoCliente: CPF
    analiseCredito: Score
}

// Contexto: Transações
Classe Conta {
    saldo: Decimal
    limite: Decimal
    transacoes: List<Transacao>
}

2. Identificando os Limites: Estratégias Práticas

A fronteira de um Bounded Context se revela onde a linguagem muda. Se o time de vendas chama "Cliente" de "Lead" e o time de suporte chama de "Usuário", há dois contextos.

Mapeamento de fluxos de negócio ajuda a identificar onde eventos e comandos cruzam fronteiras. Quando um pedido gera uma cobrança, o evento "PedidoConfirmado" pertence ao contexto de Pedidos, enquanto "CobrançaEfetuada" pertence ao contexto de Pagamento.

Event Storming é a técnica mais eficaz para revelar contextos ocultos. Reúna especialistas de domínio e times técnicos em uma sala. Cole post-its laranja para eventos do domínio. Onde houver confusão sobre o significado de um evento, provavelmente há uma fronteira de contexto.

Fluxo identificado via Event Storming:
[ClienteAdicionaProduto] -> [CarrinhoAtualizado] -> [PedidoConfirmado]
    ↑ Contexto Catálogo          ↑ Contexto Carrinho    ↑ Contexto Pedidos

3. Relacionamentos entre Bounded Contexts

Contextos não vivem isolados. Eles se relacionam de formas específicas:

Customer/Supplier: um contexto fornece dados que outro consome. O contexto de Catálogo fornece preços para o contexto de Pedidos.

Conformist: um contexto aceita passivamente o modelo do outro para evitar complexidade. Útil quando o fornecedor é maduro e estável.

Anticorruption Layer (ACL): uma camada de tradução que protege o modelo do contexto consumidor das complexidades do fornecedor. Essencial quando integrações com sistemas legados são inevitáveis.

Shared Kernel: área compartilhada entre contextos. Risco alto de acoplamento, mas útil para entidades verdadeiramente compartilhadas.

Context Map do sistema bancário:
┌─────────────────┐     ACL     ┌──────────────────┐
│  Contas a Pagar │◄────────────│  Sistema Legado   │
│  (modelo limpo) │             │  (ERP antigo)     │
└─────────────────┘             └──────────────────┘
         │
         │ Shared Kernel (Cliente)
         ▼
┌─────────────────┐
│  Cobrança       │
└─────────────────┘

4. Erros Comuns na Definição de Contextos

Contextos muito grandes criam um monólito disfarçado. Um único contexto que abrange "todo o sistema de vendas" provavelmente esconde múltiplos subdomínios com linguagens diferentes.

Contextos muito pequenos geram microserviços artificiais. Separar "Validação de CPF" em um contexto próprio é excesso de granularidade. Validação de CPF é uma regra técnica, não um contexto de domínio.

Confundir subdomínio de suporte com contexto principal leva a investimento excessivo em áreas periféricas. O subdomínio de suporte "Notificação por Email" não precisa do mesmo rigor que o core "Processamento de Pedidos".

Ignorar a evolução é o erro mais custoso. Contextos mudam conforme o negócio muda. Um contexto que nunca é revisitado acumula dívida técnica e perde alinhamento com o domínio.

5. Bounded Context e Arquitetura de Software

Cada Bounded Context pode ser implementado como um hexágono na Arquitetura Hexagonal. O modelo de domínio fica no centro, protegido por portas e adaptadores. As fronteiras do hexágono correspondem exatamente aos limites do contexto.

Integração via Event-Driven respeita as fronteiras. Quando um evento cruza contextos, ele carrega apenas os dados necessários, não o modelo completo. O contexto receptor interpreta o evento conforme seu próprio modelo.

CQRS dentro de um contexto separa operações de leitura e escrita sem quebrar o limite. O comando "CriarPedido" e a query "ListarPedidosDoCliente" pertencem ao mesmo contexto, mesmo que usem bancos diferentes.

Arquitetura Hexagonal do Contexto Pedidos:
┌─────────────────────────────────────┐
│  Adaptadores Web / API              │
│  ┌───────────────────────────────┐  │
│  │  Portas de Entrada           │  │
│  │  ┌─────────────────────────┐ │  │
│  │  │  Domínio: Pedidos       │ │  │
│  │  │  - Entidades            │ │  │
│  │  │  - Regras de Negócio    │ │  │
│  │  │  - Serviços de Domínio  │ │  │
│  │  └─────────────────────────┘ │  │
│  │  Portas de Saída             │  │
│  └───────────────────────────────┘  │
│  Adaptadores Banco / Eventos        │
└─────────────────────────────────────┘

6. Exemplo Prático: E-commerce em Bounded Contexts

Um e-commerce típico pode ser dividido em:

  • Catálogo: gerencia produtos, categorias, preços e estoque
  • Carrinho: mantém itens temporários antes da compra
  • Pedidos: processa confirmação, cancelamento e histórico
  • Pagamento: gerencia transações financeiras e antifraude
  • Logística: cuida de endereços, fretes e entregas
  • Faturamento: emite notas fiscais e gerencia cobranças

Cada contexto tem seu próprio modelo de "Cliente" e "Produto":

// Contexto Catálogo
Cliente {
    id: String
    preferencias: List<Categoria>
    historicoVisualizacao: List<Produto>
}

Produto {
    sku: String
    nome: String
    preco: Decimal
    categorias: List<Categoria>
    estoque: Int
}

// Contexto Pedidos
Cliente {
    id: String
    enderecoEntrega: Endereco
    pedidosAnteriores: List<Pedido>
}

Produto {
    sku: String
    quantidade: Int
    precoUnitario: Decimal
}

Mapa de Contexto final:

Catálogo ──Evento: ProdutoAdicionado──► Carrinho ──Evento: PedidoConfirmado──► Pedidos
   │                                                                            │
   │                                                                   ACL      │
   ▼                                                                            ▼
Pagamento ◄──Evento: CobrancaSolicitada─────────────────────────────── Pagamento
   │
   ▼
Logística ◄──Evento: PagamentoConfirmado

A ACL entre Pagamento e Logística traduz o modelo de "Endereço de Cobrança" para "Endereço de Entrega", com regras diferentes de validação.

7. Manutenção e Evolução dos Contextos ao Longo do Tempo

Contextos precisam ser revisitados. Quando o negócio adiciona "Assinatura Mensal" ao e-commerce, o contexto de Pagamento pode precisar de um split: um contexto para "Pagamento Avulso" e outro para "Assinaturas".

Monitoramento de acoplamento é essencial. Se chamadas diretas entre contextos aumentam, ou se eventos carregam dados de outros contextos, há vazamento. Métricas como "número de adapters compartilhados" ou "tamanho médio dos eventos" ajudam a detectar.

Documentação viva mantém o Context Map atualizado. Ferramentas como Context Mapper ou diagramas versionados no repositório garantem que o mapa reflita a realidade. Toda mudança de fronteira deve atualizar o mapa.

Checklist de revisão semestral de contextos:
1. A linguagem ubíqua ainda é consistente dentro do contexto?
2. Novos fluxos de negócio exigem novos contextos?
3. Há acoplamento excessivo entre contextos existentes?
4. O tamanho do contexto ainda é adequado?
5. As integrações ainda seguem os padrões definidos?

A definição correta de Bounded Contexts não é um exercício único. É um processo contínuo de descoberta e refinamento, onde o alinhamento com o domínio de negócio dita as fronteiras, e a arquitetura segue essas fronteiras com disciplina.

Referências