Implementando Domain-Driven Design
Tags: ddd, designpattern, dotnet
Published: 2020-04-22 12:53:44

---
title: Implementando Domain-Driven Design
published: true
tags: ddd, design pattern, dotnet
series: ddd
cover_image: https://images.pexels.com/photos/574071/pexels-photo-574071.jpeg?auto=compress&cs=tinysrgb&dpr=3&h=750&w=1260D
---
Após vermos os conceitos do DDD com os [Strategic Patterns](https://dev.to/guisfits/easycommerce-01-introducao-ao-ddd-kik), está na hora de ver algum código. Neste artigo abordarei os Tactical Patterns.

## Tactical Patterns

O objetivo dos **Tactical Patterns** é gerenciar a complexidade e garantir a expressividade do _Domain Model_. 

Eric Evans agrupou alguns **Design Patterns (Building Blocks)** em seu livro, que são um conjunto de boas práticas em torno dos princípios de OOP. Todos esses patterns já existiam antes de Evans os agrupar e não são exclusivos do DDD, porém Evans conseguiu consolidá-los de tal maneira que se tornaram uma ferramenta poderosa quando queremos criar um design rico e expressivo em domínios complexos.

### Entity

Uma _Entity_ é um objeto que mantém um ciclo de vida independente do seu estado atual. Seus atributos podem mudar, porém podemos sempre identificá-las por algum campo específico, como um número inteiro ou um _GUID_, por exemplo.

Vamos pegar de exemplo uma cesta de compras de um e-commerce. Após criarmos uma _Entity_ **Basket** ela naturalmente passará por mutação, o valor total, quantidade de itens e outros campos podem receber outros valores, porém sempre saberemos diferenciá-la pelo seu **Id**.

Em uma modelagem _Table Oriented_, onde cria-se classes com objetivo de representar tabelas em um banco relacional, deixa-se de considerar sua representatividade para o domínio, onde essas **classes anêmicas** são simplesmente sacolas de dados.

> Você deve ser capaz de criar todo o seu domínio e testá-lo sem sequer saber qual banco de dados irá usar, seja relacional ou NoSQL.

A _Entity_ é uma das peças fundamentais no nosso _Domain Model_ e devemos criá-la exclusivamente para satisfazê-lo. Ela é composta não apenas por atributos primitivos (int, float, string...), mas também por outras _Entities_ e objetos complexos - _Value Objects_.

É importante mantermos sua consistência. Em linguagens orientadas a objeto devemos tornar seus atributos privados de modo a blindá-los de modificações externas. Isso vai garantir que toda a mudança de estado passe por um método que aplicará possíveis regras.

A própria _Entity_ deve saber se construir. É uma má-pratica não ter um construtor neste caso, pois se deixamos classes externas criar objetos sem considerar suas regras, eventualmente iremos ter objetos inconsistentes.

Quando temos comportamento correspondente a uma _Entity_, quem melhor para tratá-los do que ela mesma? É comum em modelagens anêmicas deixar todo o comportamento para classes externas, porém acabamos somente quebrando a classe em N pedaços e dividindo sua responsabilidade. Se algo do mundo real tem estado e comportamento, uma _Entity_ deve representá-lo fielmente.

![entity](https://dev-to-uploads.s3.amazonaws.com/i/elwf8v4lhsa0nclcmpyi.png)

### Value Object

Diferentemente da _Entity_, um _Value Object_ é definido por seus atributos.

Tome o clássico exemplo da classe **Money**. O que difere duas instâncias com o mesmo valor? Nada! Eles não mantêm um ciclo de vida, sendo criados, utilizados e descartados! Também são plenamente substituíveis por outro objeto do mesmo tipo e valor.

_Value objects_ são imutáveis por definição. Assim como no mundo real você não consegue transformar R$ 5,00 em R$ 50,00, _Value Objects_ também não permitem esta mudança. Para conseguir um novo valor devemos criar uma nova instância de seu tipo, nunca alterar seus atributos internos.

Uma _Entity_ pode ser composta por N _Value Objects_. Como _Value Objects_ não mantêm estado, não temos como identificá-los no banco de dados, portanto devemos persisti-lo junto a _Entity_, nunca separado.

> Em bancos de dados orientado a documentos podemos salvá-lo dentro do mesmo objeto. Em bancos de dados relacionais podemos usar um ORM para mapear esses objetos para colunas específicas.

![value-object](https://dev-to-uploads.s3.amazonaws.com/i/tdij33xtsalizcwsbe4e.png)

### Domain-Service

Quando temos um comportamento não atrelado a uma _Entity_ ou _Value Object_, podemos representá-lo como um _Domain Service_. 

_Domain Service_ tem as seguintes características:
- Não mantêm estado
- Não tem rastreabilidade
- Podem receber _Entities_ e _Value Objects_ como parâmetro
- Podem ter dependências de interfaces para serviços externos
- Executam alguma regra de negócio.

Tome como exemplo o cálculo de taxa de envio de um pedido. Ele deve considerar os itens do carrinho, o peso de cada item e a distância do endereço de entrega. Logo, percebemos que toda essa lógica não se encaixa em nenhuma _Entity_ ou _Value Object_. Um _Domain Service_ é perfeito neste caso.

É importante mantermos a _Ubiquitous Language_ sempre em mente e aplicá-la aqui também, tanto no nome da classe e funções, quanto internamente - em nome de variáveis, por exemplo. Você pode pedir para uma pessoa de negócio explicar toda a regra e traduzir esse entendimento para o código. Assim se algo estiver faltando você facilmente conseguirá descrever o algoritmo para o _domain expert_ e receber um feedback de possíveis inconsistências.

![domain-service](https://dev-to-uploads.s3.amazonaws.com/i/y6d6a68ak5qlukvot8ja.png)

### Aggregate

Para expressar uma ideia no código é comum ter uma árvore de classes que se relacionem e possuem dependências uma da outra. Porém, conforme o _Domain Model_ se expande esta árvore acaba se tornando muito extensa e com várias ramificações. Isso dificultará seu carregamento do banco de dados, suas invariâncias ao fazer alterações e problemas de concorrência.

![Aggregate-1](https://dev-to-uploads.s3.amazonaws.com/i/wfwbjtkcl2f9xpb6f0vg.png)

Com _Aggregates_ conseguimos diminuir a complexidade de objetos grandes para pequenos grupos de classes que devem ter operações atômicas. 

> Uma operação atômica é uma ação que ocorre completamente independente de outro processo. Uma _transaction_ num banco de dados relacional segue esse princípio. Assim que você inicia uma _transaction_, nenhum outro processo poderá ler ou gravar um novo valor até que o processo anterior seja finalizado, desta forma se garante a consistência da operação e dos dados.  

Um _Aggregate_ se comporta como uma única unidade em memória, tendo sempre uma _Entity_ principal que será o _Aggregate Root_ e a partir dela teremos acesso a outras _Entities_ e _Value Objects_. _Aggregates_ podem se relacionar mantendo o **Id** de outro _Aggregate_ em seus atributos, mas nunca uma referência para outro objeto _Aggregate_.

Essa divisão deve seguir os conceitos do domínio e não apenas agrupar dados. Pense em suas invariâncias. Um **Address** só pode ser alterado se soubermos a quem ele pertence, então não faz sentido **Address** ser um objeto isolado já que ele está fortemente acoplado com **Customer**. Por sua vez, **Customer** é uma _Entity_ forte, dado que conseguimos outras informações através dela. Neste cenário faz sentido termos um _Aggregate_ chamado **Customers**, sendo a _Entity_ **Customer** seu _Aggregate Root_.

![Aggregate-2](https://dev-to-uploads.s3.amazonaws.com/i/7u11rynz5r4498dym11v.png)

> Em bancos orientado a documentos temos a facilidade de poder persistir um Aggregate por documento e obtê-lo de uma única vez.

```
FakeCommerce.Store.Shopping.Domain
├── Baskets
│   ├── Basket.cs
│   ├── BasketItem.cs
│   └── Coupon.cs
├── Customers
│   ├── Address.cs
│   ├── CreditCart.cs
│   └── Customer.cs
└── Products
    ├── Price.cs
    ├── Product.cs
    └── Size.cs
```

### Factory

Se um _Aggregate_ é suficientemente complexo para ser construído podemos usar o pattern _Factory_. Ele é um dos patterns originais do [GOF](https://pt.wikipedia.org/wiki/Padr%C3%A3o_de_projeto_de_software#Padr%C3%B5es_GoF_('Gang_of_Four')). Seu objetivo é criar objetos respeitando suas invariâncias, ou seja, ele só criará objetos que respeitem certas regras e estejam em estado válido.

Por padrão, devemos usar o método construtor presente em muitas linguagens. Uma _Factory_ é útil somente em ocasiões em que o construtor fica sobrecarregado com regras, ou quando temos mais de uma maneira de construir o objeto.

![factory](https://dev-to-uploads.s3.amazonaws.com/i/cm4qxsxix3k9iipps34f.png)

### Repository

Agora que temos os objetos organizados por _Aggregates_ precisamos de um meio para persistí-los e hidratá-los. O pattern _Repository_ é um dos mais conhecidos e seu objetivo é abstrair o acesso a dados da camada de domínio. Ele se comporta como uma coleção de dados em memória. Para quem está consumindo é transparente, além de não criar acoplamento com o meio de persistência.

Devemos ter um _Repository_ por _Aggregate_. Como o _Aggregate_ se comporta como uma unidade, não faz sentido ter um _Repository_ para cada classe dele, faz mais sentido persistir e hidratar esses objetos juntos.

O uso de _Repositories_ acaba sendo extrapolado em alguns casos. Ele é bastante útil quando precisamos hidratar os dados na camada de domínio e para reaproveitar operações comuns, porém seu uso não é obrigatório. Se você precisa extrair um relatório do sistema ou obter dados, formatar e enviar através de uma API, por exemplo, um _Repository_ acaba não ajudando muito, pois não se torna tão flexível. É importante mantermos o senso crítico e somente usá-lo quando trazer algum benefício.

Para usá-lo adequadamente basta criar uma interface na pasta do _Aggregate_ e definir os métodos. Lembre-se de usar a _Ubiquitous Language_ e evitar o nome do pattern na camada de domínio. Eu geralmente crio as interfaces com o nome do _Aggregate_ no plural, **ICustomers**, por exemplo, assim indico o seu comportamento (uma coleção em memória) sem sujar a linguagem.

![repository](https://dev-to-uploads.s3.amazonaws.com/i/bj7xzsrv78kx26tlujgf.png)

Agora, com _Dependency Inversion_, podemos receber esta interface pelo construtor sem conhecer sua implementação que está na camada de infraestrutura.

### Domain-Event

_Domain Events_ são muito úteis quando queremos propagar ações ocorridas dentro do _Domain Model_.

Esse pattern se dá na criação de uma classe _POCO_ com os dados que queremos propagar. Todo o _Domain Event_ lançado já aconteceu, refletindo o passado. _Events_ são imutáveis, já que uma vez que este evento ocorreu não tem como voltar e alterá-lo. Eles seguem o princípio _Fire and Forget_, já que uma vez lançado não temos mais controle de quem irá receber, podendo ser uma ou mais classes, devido a isso também eles não possuem nenhum retorno.

Temos algumas maneiras de implementá-lo:
- Se o _Domain Event_ será somente de uso interno - dentro do mesmo _Bounded Context_, podemos usar uma biblioteca como **MediatR**. Ela já possui uma interface chamada **INotification** que será aplicada no _Domain Event_ e outra **INoficationHandler** que será aplicada em quem esta ouvindo este evento. Assim toda vez que você disparar um novo _Domain Event_ todos os _Handlers_ irão ser chamados imediatamente, pois tudo isso acontece em memória dentro do mesmo processo. 
- Se o _Domain Event_ se propaga para outros _Bounded Contexts_ podemos usar uma solução mais robusta, como o **RabbitMQ**, por exemplo. Com ele não precisamos criamos acoplamento entre os _Bounded Contexts_, e seus Handlers podem ficar em processos separados, aumentando a escalabilidade da aplicação.

![domain event](https://dev-to-uploads.s3.amazonaws.com/i/xwbf0jigenvbnc0dxzh6.png)
![domain event handler](https://dev-to-uploads.s3.amazonaws.com/i/9zgrvgeu3d6ii3o5ue1p.png)

### Modules

E por último, mas não menos importante, vamos falar de _Modules_.

Para criar _Modules_ em `C#` temos os `namespace`, que nada mais são do que um caminho virtual para as classes. Temos também os arquivos de extensão `.csproj`, esses servem para separar os arquivos fisicamente e gerenciar suas dependências. Outras linguagens tem conceitos parecidos - como `packages` em `Java` - mas só o caminho real já pode ser o suficiente na maioria dos casos.

Com o uso de _Modules_ conseguimos manter a organização do projeto de maneira consistente e respeitar a estrutura da _Ubiquitous Language_. Esta estrutura deve nos dizer sua intenção e falar a linguagem do negócio ao mesmo tempo. Essa organização, entre outras coisas, ajuda:

- A entender assuntos relacionados e manter assuntos distintos fisicamente separados.
- Na manutenção, pois quando você precisar mexer em uma área saberá exatamente onde procurar.
- A novos desenvolvedores entender o projeto e saber onde devem ou não mexer.
- A evitar conflitos de classes com o mesmo nome.

_Modules_ são usados para decompor o _Domain Model_. Um domínio grande terá múltiplos _Bounded Contexts_ e esses terão múltiplos conceitos. Podemos considerar esses conceitos como os _Modules_. Por exemplo, quando o _domain expert_ da **Store** fala sobre **Shopping**, espera-se que todo o código relacionado a este assunto esteja em um namespace como `{CompanyName}.Store.Shopping`. _Modules_ se mantém entre o _Bounded Context_ e os _Aggregates_.

Não há uma regra dizendo como você deve arquitetar seu projeto, isso é muito subjetivo e tem N fatores que influenciam, mas de modo a ilustrar um exemplo seguindo a divisão em camadas proposta por alguns autores, podemos ter algo parecido com isso:

```
└── FakeCommerce.Store
    └── FakeCommerce.Store.Shopping
        ├── FakeCommerce.Store.Shopping.Application
        ├── FakeCommerce.Store.Shopping.Domain
        ├── FakeCommerce.Store.Shopping.Integration
        └── FakeCommerce.Store.Shopping.Persistence
```

- **Application** onde ficará os casos de uso e regras da aplicação.
- **Domain** terá os _Aggregates_ e as regras de negócio.
- **Integration** será responsável pela comunicação com outros _Bounded Contexts_ (Anti-Corruption Layer) e serviços externos.
- **Persistence** cuidará da infraestrutura do banco de dados e terá a implementação dos _Repositories_. 

Desta forma, garantimos que o domínio fique isolado do restante da aplicação e que só tenha classes com regras de negócio, sem se preocupar com detalhes de implementação que irão ficar em outras camadas.

## Considerações Finais

Os _Building Blocks_ são patterns poderosos para nos auxiliar a construir um _Domain Model_ que satisfaça as necessidades do _Domain_.

Seja pragmático no seu uso, saber não é dever. Cada pattern tem sua utilidade na construção do _Domain Model_ e eles devem ser usados à medida que as necessidades surgem, não antes. Deixe o domínio emergir naturalmente, já que a tendência é começar simples e ir ficando complexo com o tempo. Essas ocasiões são perfeitas para rever o que foi implementado e refatorar o que for necessário.

Pratique! Criei um projeto para exercitar esses patterns e aproveite para se aprofundar mais em cada um conforme as dúvidas surgirem. É nessas horas que você realmente entende o problema que cada um resolve e o seu valor como pattern.

## Referências

- [Patterns, Principles, and Practices of Domain-Driven Design](https://www.amazon.com.br/Patterns-Principles-Practices-Domain-Driven-English-ebook/dp/B00XLYUA0W/ref=sr_1_1?__mk_pt_BR=%C3%85M%C3%85%C5%BD%C3%95%C3%91&keywords=Patterns%2C+Principles%2C+and+Practices+of+Domain-Driven+Design&qid=1587221702&s=digital-text&sr=1-1)
- [What are Modules in Domain Driven Design?](https://culttt.com/2014/12/10/modules-domain-driven-design/)