Context mapping no DDD: integrando bounded contexts
1. Fundamentos do Context Mapping
1.1. Definição de Bounded Context e sua importância na delimitação de domínios
No Domain-Driven Design (DDD), um Bounded Context é um limite explícito dentro do qual um modelo de domínio é definido e aplicado. Cada contexto possui sua própria linguagem ubíqua, suas regras de negócio e suas entidades, que podem ter significados diferentes em contextos distintos. Por exemplo, o termo "Cliente" no contexto de Vendas pode incluir dados de faturamento, enquanto no contexto de Suporte pode referir-se apenas ao histórico de chamados. Essa delimitação evita ambiguidades e permite que cada equipe trabalhe com autonomia.
1.2. O papel do Context Map como ferramenta de integração e governança
O Context Map é um artefato que documenta as relações entre diferentes Bounded Contexts. Ele funciona como um mapa de integração, identificando quais contextos se comunicam, qual o tipo de relacionamento e quais padrões de integração são aplicados. Sem um Context Map, equipes podem criar dependências ocultas, gerar acoplamento excessivo e comprometer a evolução do sistema.
1.3. Relação entre Context Mapping e padrões de Arquitetura de Software (ex.: microsserviços)
Context Mapping é fundamental para arquiteturas baseadas em microsserviços, onde cada serviço geralmente corresponde a um Bounded Context. A escolha do padrão de relacionamento entre contextos impacta diretamente o nível de acoplamento, a latência das comunicações e a resiliência do sistema. Um Context Map bem definido orienta decisões como: "este contexto deve expor uma API REST?" ou "devemos usar eventos assíncronos para integrar esses dois contextos?".
2. Padrões de Relacionamento entre Contextos
2.1. Parceria (Partnership): colaboração bidirecional e sincronização
No padrão Partnership, dois contextos colaboram de forma coordenada para atingir objetivos comuns. Ambos evoluem em conjunto, sincronizando mudanças e alinhando cronogramas. É útil quando há forte interdependência entre times, como em uma funcionalidade que exige alterações simultâneas em dois contextos.
Exemplo textual de Partnership:
Contexto de Pedidos <--> Contexto de Estoque
- Ambos os times planejam sprints juntos
- Alterações em regras de reserva de estoque são comunicadas e aplicadas simultaneamente
2.2. Shared Kernel: compartilhamento controlado de modelo entre contextos
Shared Kernel permite que dois contextos compartilhem uma parte do modelo de domínio, como entidades e serviços comuns. Esse compartilhamento é feito sob controle rigoroso de versão e testes, para evitar que mudanças em um contexto quebrem o outro. É indicado quando contextos precisam de um núcleo comum, mas desejam manter autonomia.
Exemplo textual de Shared Kernel:
Contexto de Faturamento <--> Contexto de Contabilidade
- Compartilham a entidade "TransacaoFinanceira"
- Qualquer alteração no modelo exige aprovação de ambas as equipes
2.3. Cliente-Fornecedor (Customer-Supplier): dependência unilateral com contratos
Neste padrão, um contexto (fornecedor) provê dados ou serviços para outro (cliente). O cliente depende do fornecedor, mas o fornecedor não depende do cliente. Para garantir estabilidade, o fornecedor define contratos explícitos (APIs, interfaces) que o cliente consome. O cliente pode influenciar o roadmap do fornecedor, mas não controla suas mudanças.
Exemplo textual de Customer-Supplier:
Contexto de Catálogo (Fornecedor) --> Contexto de Vitrine (Cliente)
- Fornecedor expõe API REST com produtos e preços
- Cliente consome a API e adapta os dados conforme necessário
3. Padrões de Isolamento e Proteção
3.1. Conformista (Conformist): adaptação ao modelo do fornecedor sem tradução
Quando o cliente aceita o modelo do fornecedor sem realizar tradução, adota-se o padrão Conformist. Isso simplifica a integração, mas pode introduzir acoplamento semântico. É adequado quando o fornecedor é estável e seu modelo é suficientemente próximo do que o cliente precisa.
Exemplo textual de Conformist:
Contexto de CRM (Fornecedor) --> Contexto de Marketing (Cliente)
- Marketing consome diretamente o modelo de "Lead" do CRM
- Não há camada de tradução; Marketing aceita os campos como são
3.2. Anti-Corruption Layer: tradução e proteção contra contaminação de legados
O Anti-Corruption Layer (ACL) é um padrão que insere uma camada de tradução entre dois contextos, protegendo o modelo do cliente de contaminações do modelo do fornecedor. É essencial quando um contexto legado possui modelo confuso ou inconsistente que não se deseja propagar.
Exemplo textual de Anti-Corruption Layer:
Contexto de Legado (ERP antigo) --> ACL --> Contexto de Pedidos (novo)
- ACL traduz entidades do ERP (ex.: "Cliente_Antigo") para o modelo do novo contexto
- O novo contexto nunca acessa diretamente o banco legado
3.3. Open-Host Service: exposição de interfaces públicas e linguagem ubíqua
Neste padrão, um contexto expõe uma interface pública (geralmente uma API) que utiliza sua própria linguagem ubíqua. Clientes se comunicam através dessa interface, sem conhecer os detalhes internos do contexto. É o padrão mais comum em microsserviços bem projetados.
Exemplo textual de Open-Host Service:
Contexto de Pagamentos expõe API REST:
- POST /pagamentos
- GET /pagamentos/{id}
- A API usa termos como "Transacao", "StatusPagamento", "Valor"
4. Padrões de Separação Total
4.1. Separate Ways: independência completa sem integração direta
Quando contextos não precisam se comunicar, adota-se Separate Ways. Cada contexto opera de forma independente, eventualmente replicando dados necessários por meio de processos assíncronos ou ETLs. Esse padrão reduz acoplamento ao mínimo.
4.2. Big Ball of Mud: identificação e mitigação de contextos não estruturados
Big Ball of Mud descreve um contexto que cresceu sem governança, misturando responsabilidades e modelos inconsistentes. O Context Map deve identificar esses pontos e planejar refatorações, como a extração de subcontextos ou a aplicação de ACL.
4.3. Quando optar por Separação vs. Integração: trade-offs arquiteturais
A decisão entre separação e integração envolve trade-offs como: latência (integração síncrona vs. assíncrona), consistência (eventual vs. imediata) e complexidade operacional. Contextos que evoluem em ritmos diferentes ou que pertencem a domínios distintos geralmente se beneficiam de maior separação.
5. Mapeamento e Documentação de Context Maps
5.1. Técnicas de levantamento: entrevistas, event storming e análise de código
Para construir um Context Map, é necessário entender as interações reais entre contextos. Técnicas como Event Storming (workshop colaborativo) ajudam a identificar eventos de domínio e fluxos. Entrevistas com desenvolvedores e análise de código (rastreamento de chamadas de API, filas, bancos) complementam o levantamento.
5.2. Notação visual: diagramas de Context Map e relações entre contextos
Diagramas de Context Map representam contextos como caixas e as relações como setas rotuladas com o padrão utilizado (Partnership, ACL, etc.). A notação visual facilita a comunicação entre times e stakeholders.
5.3. Ferramentas práticas: exemplos de representação em texto e diagramas
Ferramentas como Miro, Lucidchart ou até mesmo diagramas em Markdown (com Mermaid) podem ser usadas. Exemplo textual de representação:
[Contexto de Vendas] -- ACL --> [Contexto de Estoque]
[Contexto de Estoque] -- Partnership --> [Contexto de Logística]
[Contexto de Logística] -- Open-Host Service --> [Contexto de Rastreio]
6. Integração Técnica entre Contextos
6.1. Estratégias de comunicação: eventos, mensageria e APIs REST
A comunicação entre contextos pode ser síncrona (REST, gRPC) ou assíncrona (eventos, filas). Eventos são preferíveis quando se deseja baixo acoplamento e consistência eventual. APIs REST são adequadas para consultas e comandos que exigem resposta imediata.
6.2. Tratamento de consistência eventual e transações distribuídas
Em sistemas distribuídos, a consistência eventual é comum. Padrões como Saga (coreografia ou orquestração) gerenciam transações distribuídas sem locks globais. O Context Map deve documentar quais contextos toleram inconsistência temporária.
6.3. Exemplo de código: Anti-Corruption Layer em cenário de legado
Exemplo textual de ACL em Python (pseudocódigo):
# Contexto Legado (ERP antigo)
class ERPCliente:
def get_cliente(self, id):
# Retorna dicionário com campos como "nome_cliente", "endereco_antigo"
return {"nome_cliente": "João", "endereco_antigo": "Rua A, 123"}
# ACL (Anti-Corruption Layer)
class ClienteACL:
def __init__(self, erp_cliente):
self._erp_cliente = erp_cliente
def obter_cliente(self, id):
dados_erp = self._erp_cliente.get_cliente(id)
# Tradução para o modelo do novo contexto
return {
"nome": dados_erp["nome_cliente"],
"endereco": self._traduzir_endereco(dados_erp["endereco_antigo"])
}
def _traduzir_endereco(self, endereco_antigo):
# Lógica de tradução
return endereco_antigo.replace("antigo", "novo")
# Novo contexto de Pedidos
class PedidoService:
def __init__(self, cliente_acl):
self._cliente_acl = cliente_acl
def criar_pedido(self, cliente_id):
cliente = self._cliente_acl.obter_cliente(cliente_id)
# Usa o modelo limpo do cliente
print(f"Pedido criado para {cliente['nome']} em {cliente['endereco']}")
7. Evolução e Governança do Context Map
7.1. Context Mapping como prática contínua em arquiteturas evolutivas
O Context Map não é estático. À medida que o sistema evolui, novos contextos surgem, outros são fundidos ou quebrados. Revisões periódicas do mapa ajudam a identificar degradação de padrões (ex.: Partnership virando Shared Kernel não documentado).
7.2. Métricas de acoplamento e coesão entre contextos
Métricas como número de dependências entre contextos, frequência de chamadas e tempo de resposta podem indicar acoplamento excessivo. Ferramentas de observabilidade (APM, tracing distribuído) auxiliam na coleta desses dados.
7.3. Refatoração de Context Maps: quebra e fusão ao longo do tempo
Quando um contexto cresce demais, pode ser quebrado em subcontextos. Por outro lado, contextos que sempre mudam juntos podem ser fundidos. A refatoração deve ser planejada com base em evidências de acoplamento e alinhamento com a estratégia de negócio.
8. Considerações Finais e Boas Práticas
8.1. Armadilhas comuns: superintegração e subestimação de custos de tradução
Superintegrar contextos (ex.: usar Partnership desnecessariamente) pode gerar acoplamento e perda de autonomia. Subestimar o custo de manter uma ACL pode levar a codebases complexos. O Context Map ajuda a equilibrar esses trade-offs.
8.2. Alinhamento com estratégia de negócio e times de desenvolvimento
O Context Map deve refletir a estrutura organizacional (Lei de Conway). Times autônomos geralmente correspondem a contextos independentes. A integração entre contextos deve ser mínima e bem documentada.
8.3. Checklist para implementação de Context Mapping em projetos reais
- Identifique todos os Bounded Contexts do sistema.
- Documente as relações entre eles (padrões de integração).
- Defina contratos explícitos para cada interface.
- Implemente camadas de proteção (ACL) quando necessário.
- Revise o Context Map a cada trimestre ou após mudanças significativas.
- Alinhe o mapa com a arquitetura de microsserviços e times.
Referências
- Domain-Driven Design: Context Mapping — Artigo de Martin Fowler sobre Bounded Context e Context Mapping, com exemplos práticos.
- Strategic Domain-Driven Design: Context Mapping Patterns — Documentação da DDD Community sobre padrões estratégicos de integração entre contextos.
- Anti-Corruption Layer: Pattern in DDD — Guia da Microsoft sobre o padrão Anti-Corruption Layer em arquiteturas de microsserviços.
- Event Storming: A Practical Guide — Site oficial do Event Storming, técnica essencial para mapeamento de contextos e eventos de domínio.
- Context Mapping in Microservices Architecture — Artigo no InfoQ sobre como aplicar Context Mapping em arquiteturas de microsserviços, com estudos de caso.
- Implementing Domain-Driven Design (Vernon) — Livro de Vaughn Vernon que aprofunda Context Mapping e padrões de integração (capítulos 5 e 6).
- DDD, Hexagonal, Onion, Clean, CQRS, ... How I put it all together — Artigo que relaciona DDD estratégico com padrões arquiteturais, incluindo Context Mapping.