Domain-Driven Design, Os building blocks | PARTE 3: Domain, Subdomains e Bounded Contexts
👉 This article is also available in English on my blog
Na segunda parte dessa série de artigos sobre Domain-Driven Design, começamos a modelar o domínio de um sistema de automação de marketing. Criamos nossa primeira Entity e Value Objects, e vimos 4 erros comuns na criação destes, e como resolvê-los.
Criamos um código orientado a objetos, amplamente testado, e mais importante, refletindo a linguagem do domínio.
No final, acredito que ficou claro o motivo pelo qual é fundamental esquecer o modelo ER enquanto estivemos modelando o domínio, e a consequência de não fazê-lo.
Se você não leu, comece por lá, pois esse artigo é uma continuação do anterior:
Domain-Driven Design, Os building blocks | Parte 02: Entities e Value Objects
⚠️ Eu já falei sobre a diferença entre Domínio e Modelo de Domínio aqui. Se você não lembra, releia essa parte agora, porque isso deve estar claro para prosseguirmos com o entendimento dos próximos itens.
Hoje demonstrarei mais dois blocos de construção fundamentais do DDD: Subdomínios (Subdomains) e Contextos Delimitados (Bounded Contexts)
Subdomains
😕 Um ponto que muitas vezes gera confusão, e consequentemente um erro grande de modelagem, é achar que o Modelo de Domínio (Domain Model) deve ser um modelo único, que contemple todo o Domínio da organização. ✔️ Mas na verdade, quando estamos usando DDD, fazemos exatamente o contrário, ou seja, pensamos em uma única área do negócio de cada vez.
VERNON (2013)
Dessa forma, para organizar e entender o Domínio no qual estamos inseridos, devemos decompô-lo em Subdomínios, que geralmente refletem a estrutura organizacional do negócio. Por exemplo, vendas, estoque, remessa, contabilidade, etc.
Sabemos que o Domínio (Domain) corresponde ao universo inteiro do negócio, ou seja, o Domínio é o que uma organização faz, e o mundo em que ela o faz. Por outro lado, quando queremos nos referir a apenas uma área do negócio, a chamamos de Subdomínio (Subdomain).
Core Domain
É o Subdomínio mais importante, é aquele que justifica a existência da organização. Por exemplo, se a organização é uma fábrica de calçados, a área mais importante é a produção e venda de calçados. Esse Subdomínio que representa o coração do negócio, chama-se Core Domain. 💰
Generic Subdomains
Além do Core Domain, sabemos que existem outras áreas necessárias que fazem com que uma organização funcione. Essas áreas servem para apoiar o Core Domain na sua função. No caso da nossa fábrica de calçados, é necessário uma logística para distribuir os produtos, um RH para contratar funcionários capacitados, uma contabilidade para gerir o patrimônio, entre outras áreas. Dentro do DDD, chamamos essas áreas de Subdomínios genéricos.
Perceba que o Core Domain depende do tipo da organização. Se ao invés de uma fábrica de calçados, nossa organização fosse uma empresa de RH, o Core Domain possivelmente seria o R&S.
Porém, isso são apenas exemplos. Na vida real, tome cuidado para não fazer pressupostos antes de conhecer bem o Domínio! Se a fábrica de calçados do nosso exemplo, possuir um método de entrega super eficiente, que lhe confere um diferencial competitivo, certamente o seu processo de entrega faz parte do seu Core Domain, pois trata-se de uma estratégia importante para o negócio. 🎯
🤔 Fui só eu ou você também pensou na Amazon agora?
Bounded Contexts
💡 Contextos Delimitados (Bounded Contexts) é um dos design patterns mais difíceis de ser entendido no DDD, mas certamente é um dos mais importantes (mais importante do que ele é a Linguagem Ubíqua, e você já vai entender porquê).
Por isso vamos, passo-a-passo, explicar o que é, onde está, e para que ele serve, nesse conjunto de peças que compõem o DDD.
No Domínio / Subdomínios estão os problemas que nós queremos resolver (lembre-se que para resolvê-los nós criamos Modelos de Domínio (Domain Models)).
E é aqui que entra o papel dos Contextos Delimitados (Bounded Contexts): Enquanto que os Subdomínios delimitam o Domínio, os Contextos Delimitados delimitam os Modelos de Domínio.
💡 Colocando de outra forma: Para modelarmos um Subdomínio, a fim de resolver um problema através do nosso software, é necessário que criemos um ou mais modelos desse Subdomínio. E para organizarmos esses modelos, de acordo com a sua Linguagem Ubíqua, usamos Contextos Delimitados.
Portanto, os Subdomínios estão no campo do problema, e os Contextos Delimitados no campo da solução.
Se for necessário, podemos ter mais de um Contexto Delimitado para um Subdomínio (que é o que ocorre no nosso Marketing Subdomain):
E conforme o Vernon, “…é uma meta desejável alinhar subdomínios um a um com contextos limitados.”
Agora vejamos o modelo de domínio de um varejo on-line, que o Vernon usa para demonstrar o que ocorre quando o DDD não é aplicado corretamente, resultando em poucos Contextos Delimitados responsáveis por muitas funções de negócio:
Na figura acima temos dois Contextos Delimitados: e-Commerce e Inventory. Perceba que o Contexto Delimitado e-Commerce, contêm os Subdomínios Product Catalog, Orders, Invoicing e Shipping.
Esses Subdomínios estão interagindo entre sí, mas não há uma divisão clara entre eles — estão todos fundidos nesse Contexto Delimitado gigante chamado e-Commerce.
Por outro lado, o Contexto Delimitado Inventory contêm apenas um Subdomínio, que é uma meta desejável.
Obs.: External Forecasting é um sistema externo que está fora do modelo de domínio.
É por isso que a Linguagem Ubíqua é o mais importante do DDD. Pois se você não entendê-la logo no início da modelagem, vai modelar errado, pois não saberá quais são, nem o que contemplam os seus Contextos Delimitados.
Voltando ao nosso domínio de automação de marketing
Depois de analisar detalhadamente o problema junto com a especialista de domínio, eu percebi que o nosso modelo deveria ser dividido em dois Bounded Contexts: Capturar Leads Context e Executar Campanha Context:
💡 Observação: Como vimos anteriormente, no domínio de uma organização existem vários subdomínios. Nesta imagem eu estou representando apenas o subdomínio de marketing (especificamente, automação de marketing), pois é com ele que estamos trabalhando agora. Lembre-se: “…pensamos em uma única área do negócio de cada vez”.
O círculo mais externo representa o Domínio completo. Dentro dele temos o Subdomínio de marketing, que possuí os Bounded Contexts: Capturar Leads Context e Executar Campanha Context. Estes, por sua vez, contém os modelos (Domain Models).
💡 Perceba o papel dos Bounded Contexts, criando uma fronteira conceitual que delimita o contexto da Linguagem Ubíqua (eu falei bastante sobre a linguagem ubíqua no aqui, se você não leu, confira agora).
Observe que existem conceitos diferentes entre eles (como Ações e Componentes), mas também existem conceitos compartilhados (como Leads). Porém, embora os dois Bounded Contexts compartilhem o conceito de Leads, eles tem dados e comportamentos diferentes entre si.
O agregado Leads é duplicado entre os Bounded Contexts, porque ele tem um significado diferente em cada um deles. É perfeitamente normal que façamos isso quando encontramos conceitos compartilhados, mas que tem significados (propriedades e métodos) diferentes.
Um erro bastante comum seria fazer o contrário. Ou seja, não dividir Leads, mantendo todas as propriedades e métodos na mesma classe, apesar deles não terem relação nenhuma.
Muitas vezes encontramos esse tipo de classe que, desprezando o Principio de Responsabilidade Única, visa contemplar todos os problemas do mundo, e se torna impossível de manter (code smell: God Class). 🤬😤
Muitas vezes esse tipo de classe acaba sendo movida para o Shared Kernel (vamos falar sobre ele ainda), na tentativa de deixar explicito a intenção de usá-la de forma comum entre outros Bounded Contexts. Mas quando você abre classes assim, pela quantidade de linhas e pela falta de uma interface pública bem definida, fica claro que ela deveria ter sido dividida, pois está com excesso de responsabilidade. 🤬😤
A imagem abaixo é uma representação desse problema:
Perceba que nessa versão, Leads foi movido para o Shared Kernel, com a intenção de ser reutilizado entre os Bounded Contexts Capturar Leads Context e Executar Campanha Context. No entanto, como Leads tem uma linguagem diferente em cada um desses contextos, acabamos criando uma classe Lead com excesso de responsabilidades e que não reflete a linguagem do Subdomínio no qual está inserida. 💩
Ou seja, quando estamos usando Leads no contexto Executar Campanha Context há métodos e propriedades que não servem para nada (e que na verdade acabam estragando o design, pois nos obrigam a criar uma interface pública que contemple tudo).
Veja abaixo a classe Lead, conforme foi criada no artigo anterior. Ela está longe de estar finalizada, mas já serve bem para exemplificar o Capturar Leads Context:
Por outro lado, no Executar Campanha Context precisamos apenas do E-mail, do Nome, e dos Segmentos do Lead. Além disso, nesse contexto não podemos atualizar o Lead nem adicionar novos Segmentos a ele:
Vernon se refere a esse problema como Big Ball of Mud (a grande bola de lama). Que de fato, é um bom nome para descrever o resultado que se obtém desse tipo de modelagem all-in-one!
Conclusão
Você apreendeu que o Modelo de Domínio não é um modelo único, e compreendeu a importância de organizarmos o nosso Domínio em Subdomínios e Contextos Delimitados.
Acredito que ficou claro a diferença entre Domínio, Subdomínios e Contextos Delimitados, e para que serve cada um deles.
Dentro dos Subdomínios, você entendeu que o mais importante é o Core Domain.
E sobretudo, eu espero que você tenha percebido a importância crucial de investir tempo em descobrir e refinar a Linguagem Ubíqua, dado o papel fundamental que ela tem na modelagem.
E não se engane: Linguagem Ubíqua parece simples, mas nas aplicações complexas que temos que lidar no nosso dia-a-dia, somado ao fato de que temos que interagir com pessoas de diferentes áreas, que usam termos diferentes para se referir as mesmas coisas... é bastante trabalho.
Se você ficou com alguma dúvida, não deixe de perguntar usando a seção de comentários abaixo:
Referências
Evans, Eric. Domain-Driven Design: Tackling Complexity in the Heart of Software. 2004.
Vernon, Vaughn. Implementing Domain-Driven Design. 2013.
Maicon Heck
.NET Senior Developer | Software Craftsman | Domain-Driven Design practitioner