DDD - Integrando Bounded Contexts


 Neste artigo vou apresentar os padrões de relacionamentos do DDD usados para definir os relacionamentos e dependências entre os Bounded Context para gerar o Context Map.

Fazer a integração dos Bounded Contexts (BCs) é um dos pontos mais delicados do Domain-Driven Design (DDD), porque cada contexto é um “mundo fechado” com seu próprio modelo e linguagem ubíqua. Integrá-los exige cuidado para não criar um grande acoplamento entre eles.



Existem diferentes padrões de integração de Contextos Delimitados, descritos por Eric Evans e também detalhados por Vaughn Vernon no Implementing Domain-Driven Design.

Os Bounded contexts ou Contextos Delimitados definem um limite lógico de um domínio de negócio onde uma Linguagem Ubíqua consistente é usada. Embora os bounded contexts evoluam independentemente, eles ainda precisam colaborar para criar um sistema que gere valor para as empresas.

Antes de apresentar os padrões de relacionamentos temos que entender os alguns termos usados na nomenclatura:

Upstream/Downstream: (descrevem a direção da dependência)
- Quem fornece uma linguagem/contrato é Upstream;
- Quem consome é Downstream;
- Mudanças no Upstream afetam o Downstream;
- Upstream é o contexto “Fornecedor" (que dita as regras)
- Downstream é o contexto “Consumidor" (que se adapta).

Customer-Supplier :
- Downstream (Customer ou Consumidor)
- Upstream (Supplier ou Fornecedor)

Para os exemplos usados no artigo vamos considerar um sistema de vendas onde um Cliente navega por um Catálogo , faz um Pedido e recebe a entrega. Temos assim 3 Bounded Contexts : 

1- Gestão de Pedidos - Com Pedido, ItemPedido (PrecoUnitario), Pagamento, EnderecoEntrega (snapshot)
2- Gestão de Catálogo - Produto (Preco) e Categoria
3- Gestão de Clientes - Cliente, Endereco

Assim, o DDD define vários padrões para descrever como os Bounded Contexts se conectam. Esses padrões são representados no mapa e ajudam a classificar as relações. Vejamos a seguir a definição para cada um destes padrões.

Partnership (Parceria)

Neste modelo, a integração entre os bounded contexts é feita na hora. Se uma equipe decidir modificar sua API, ela informa as outras equipes, que farão as alterações necessárias. Sem drama ou complicações. Nesse cenário, nenhuma equipe dita a linguagem ubíqua usada; elas cooperam e encontram a solução mais adequada. As equipes também trabalham juntas para resolver rapidamente qualquer conflito de integração que possa surgir.

Este padrão funciona melhor para equipes maduras que se comunicam bem. Por outro lado, equipes distribuídas em grandes áreas geográficas podem achar essa abordagem desafiadora, pois a comunicação e a sincronização são mais complexas e demoradas.

Exemplo: Relação entre BC Pedidos e Pagamentos

No nosso sistema de vendas, uma simulação de parceria ocorreria se os BCs de Pedidos e Pagamento (se fossem separados) fossem desenvolvidos por equipes que compartilham o mesmo código, cronograma e metas.

Por exemplo, a classe Pagamento seria gerenciada de forma mútua, com as duas equipes se reunindo constantemente para garantir que as alterações de uma não quebrem a outra. É uma parceria de alto risco, mas também de alta coordenação. É o padrão mais "forte" de relacionamento.

Shared Kernel (Núcleo Compartilhado)

Na vida real, há muitas situações em que alguns modelos são usados em vários bounded contexts. Quando isso acontece, você tem duas opções: duplicação ou compartilhamento.

Usando o modelo de compartilhamento, você cria componentes que são utilizados por múltiplos bounded contexts. Quando o núcleo compartilhado é modificado, isso afeta todos os bounded contexts que dependem dele, criando um forte acoplamento.

Há algumas coisas que você pode fazer para mitigar essa desvantagem:

- Mantenha o núcleo compartilhado pequeno, o menor possível.
- Idealmente, apenas as estruturas de dados compartilhadas entre os bounded contexts devem ser mantidas no núcleo compartilhado.
- Use a Integração Contínua — cada vez que o núcleo compartilhado muda, os bounded contexts também precisam ser atualizados e verificados quanto à conformidade.

Embora este modelo crie acoplamento, ainda é uma ferramenta valiosa, especialmente quando o custo da duplicação excede o custo da integração. Se você observar que o núcleo compartilhado muda com frequência e precisa gerenciar problemas de integração, talvez seja hora de considerar a duplicação.

Exemplo: Relação entre BC Pedidos e Clientes

Imagine que Pedidos e Clientes precisam trabalhar com a mesma definição de ClienteId.
O contexto de Pedidos precisa saber qual cliente fez um pedido.
O contexto de Clientes é quem mantém os dados detalhados do cliente.

As duas equipes decidem compartilhar uma classe comum contendo a definição de um ClienteId que seria compartilhada entre ambas.

Isso, no entanto, é um risco. Se uma equipe precisar mudar a forma como o ClienteId é tratado, ela pode acidentalmente quebrar a outra equipe, o que torna esse padrão de alta manutenção e risco.

Supplier-Consumer (Fornecedor-Consumidor)

Há muitas situações em que um bounded context fornece dados ou serviços a outro. Chamamos o contexto que fornece dados/serviços de fornecedor ou bounded context upstream. Aquele que depende desses dados/serviços é chamado de consumidor ou bounded context downstream. Quando isso acontece, um dos bounded contexts geralmente é mais importante da perspectiva de negócio.

Se o fornecedor for mais importante (sua equipe tiver mais poder de decisão), a equipe que o possui ditará o contrato de integração. É uma abordagem de "aceitar ou deixar". O bounded context do fornecedor define o contrato de integração, e os consumidores devem se adaptar. Nesse cenário, você pode escolher o modelo Conformist ou o modelo ACL.

Exemplo: Imaginem que o BC de Pedidos (Customer/Cliente) precisa de dados do BC de Clientes ( Supplier/Fornecedor).

Nessa relação, a equipe do BC Pedidos negocia com a equipe de Clientes para que o que eles precisam seja fornecido.

É uma relação de dependência saudável, onde o Supplier/Fornecedor – Clientes - se compromete a atender as necessidades do Customer/Cliente - Pedidos

Conformist (Conformista)

Se a equipe downstream puder aceitar e se adaptar ao modelo da equipe upstream, seu relacionamento é chamado de conformista.

Você pode se perguntar por que uma equipe abriria mão do controle e da autonomia para outra? Se o contrato de integração fornecido pela equipe upstream aderir a um padrão reconhecido pela indústria ou se for bom o suficiente para a equipe downstream, essa pode ser uma escolha pragmática. O ego é algo perigoso no desenvolvimento de software; você precisa saber quando deixá-lo de lado.

Exemplo: Neste caso, imagine que o nosso BC de Pedidos precisa dos dados de produtos do BC de Catálogo.

A equipe de Catálogo é a 'dona' daquele modelo, e ele é muito grande e bem definido.

A equipe de Pedidos(Consumidor), em vez de negociar ou tentar mudar o modelo de Catálogo (Fornecedor) , simplesmente decide se Conformar a ele.

O Contexto de Pedidos aceita a Linguagem Ubíqua e a estrutura do Catálogo, se adaptando. É uma escolha pragmática.

Anti-Corruption Layer (ACL - Camada Anticorrupção)

Quando o bounded context downstream é um subdomínio principal (vital para o negócio), a abordagem conformista não funciona, mesmo que a balança de poder ainda penda para a equipe upstream.

Nesse caso, a equipe downstream se recusa a se conformar. Ela não pode ditar o contrato de integração, mas pode se desacoplar dele. Ela pode traduzir o contrato de integração criado pela equipe fornecedora em algo significativo e válido para ela. Essa camada de abstração protege seu bounded context da interferência externa e permite que seu modelo de domínio evolua sem depender de fatores externos.

Simplificando, uma camada anticorrupção (ACL) é apenas um intermediário que fica entre o contrato de integração externo e o modelo de domínio interno do bounded context do consumidor. Seu propósito é proteger a lógica de negócio do ruído externo e manter as coisas válidas e consistentes.

Exemplo: A relação entre o BC de Pedidos e o Gateway de Pagamento

O Gateway de Pagamento é um sistema externo que não controlamos. Ele tem um modelo de dados próprio, com termos como "transaction ID", "auth code" e "response code".

Para não poluir nosso modelo de domínio de Pedidos, criamos uma ACL que traduz esses conceitos para os nossos próprios, como Pagamento e StatusPagamento. Isso nos permite trocar o Gateway de Pagamento no futuro sem precisar refatorar todo o nosso BC de Pedidos.

Open-Host Service (OHS - Serviço de Host Aberto)

Este modelo de integração aborda casos em que o equilíbrio de poder favorece os consumidores. O fornecedor desacopla sua lógica de implementação de sua API pública para melhor atender aos consumidores. É o oposto da situação do ACL.

A interface pública do fornecedor não precisa se conformar com sua linguagem ubíqua. Em vez disso, o contrato de integração é construído para servir melhor os bounded contexts downstream.

É comum que serviços upstream exponham múltiplas versões do contrato de integração. Isso permite que os consumidores escolham o contrato que melhor se adapta às suas necessidades e migrem gradualmente para versões mais recentes.

Exemplo: Relação entre BC Catálogo e o BC Pedidos , Estoque e Marketing

O BC de Catálogo é a fonte da verdade sobre todos os produtos da nossa empresa. Ele não serve apenas o BC de Pedidos, mas também pode servir outros sistemas, como um sistema de Gerenciamento de Estoque ou um aplicativo de visualização para a equipe de Marketing.

Em vez de a equipe de Catálogo negociar três APIs diferentes (uma para Pedidos, uma para Estoque e uma para Marketing), ela decide implementar o OHS e cria uma única API pública e bem documentada que permite a todos os outros BCs consultar informações de produt

Published Language (Linguagem Publicada): É uma estratégia onde um BC Upstream se compromete a expor um formato de dados ou um protocolo de comunicação estável e bem documentado. Isso facilita a integração para múltiplos BCs Downstream, que podem consumir essa linguagem com confiança

Exemplo: Relação entre BC Clientes e BC Pedidos

O BC de Clientes decide publicar um conjunto de eventos de domínio, como ClienteCriado ou ClienteEnderecoAtualizado, usando um formato de dados estável (por exemplo, um schema JSON versionado).

O BC de Pedidos pode, então, se inscrever nesses eventos para manter seus dados de endereço de entrega atualizados. Isso permite que múltiplos BCs se integrem ao BC Clientes sem a necessidade de negociações ponto a ponto.

O BC Clientes é a fonte da verdade e o provedor de uma "linguagem" confiável para todos.

Separate Ways (Caminhos Separados)

Às vezes, o custo de integrar bounded contexts excede o tempo que leva para duplicar componentes. Se for esse o caso, a duplicação é uma escolha pragmática. Duplicar componentes, mesmo que possam ser os mesmos para múltiplos bounded contexts, também é viável se a comunicação eficaz entre as equipes não for possível. Usando essa abordagem, as equipes podem evoluir seus bounded contexts de forma independente.

Exemplo: Relação entre BC Pedidos e BC RH
.
Imagine que a empresa tem um sistema de RH para gerenciar a folha de pagamento e os benefícios dos funcionários. Esse sistema tem seu próprio domínio de "Funcionários" e "Salários". A equipe de Vendas (e nosso BC de Pedidos) não precisa de nenhuma informação desse sistema.

O modelo de funcionário do RH é completamente irrelevante para o nosso sistema de vendas. Portanto, o relacionamento entre o BC de Pedidos e o BC de RH pode ser definido como Separate Ways pois eles operam em domínios completamente separados e não há necessidade de comunicação.

Conclusão

Espero ter convencido você de que não existe um modelo de integração certo ou errado entre os bounded contexts. Cada abordagem tem prós e contras e é adequada para um cenário específico.

Também estou ciente de que minha apresentação desses modelos de integração não é um aprofundamento, mas um bom ponto de partida, especialmente se você é um novo praticante de DDD.

Cabe destacar que a integração de Bounded Contexts não é sobre tecnologia, mas sobre relacionamento entre equipes e domínios. A tecnologia (eventos, APIs, ACL) vem depois, para implementar o padrão de relacionamento escolhido.

No meu canal no youtube estou publicando uma série sobre o DDD.

E estamos conversados...  

"Bendito o Deus e Pai de nosso Senhor Jesus Cristo, o qual nos abençoou com todas as bênçãos espirituais nos lugares celestiais em Cristo;"
Efésios 1:3

Referências:


José Carlos Macoratti