Skip to content

Instantly share code, notes, and snippets.

@danilobatistaqueiroz
Last active January 14, 2023 22:52
Show Gist options
  • Save danilobatistaqueiroz/be98a020ebc142b075322d7f3fa01384 to your computer and use it in GitHub Desktop.
Save danilobatistaqueiroz/be98a020ebc142b075322d7f3fa01384 to your computer and use it in GitHub Desktop.
DDD - Domain Driven Design

DDD é um conjunto de boas práticas de orientação a objetos e um padrão de modelagem de sistemas, uma metodologia para aplicação de conceitos e padrões no projeto.

DDD tende a ser uma idéia geral focando em entender primeiro o domínio.
Os problemas em questão, os objetos com os quais está trabalhando.
São construídos os modelos primeiro, ferramentas para manipulá-los em segundo lugar, e depois a lógica de negócios no topo para resolver os problemas de domínio por exigência de negócios.
Normalmente trabalha em mecanismos de persistência em seguida, e apresentação por último.

Isso vem em contrapartida com o uso comum do Data-Driven Design ou Projeto Orientado a Dados, que a maioria dos desenvolvedores usa sem mesmo ter consciência disso. Devemos em um primeiro momento esquecer de como os dados são persistidos e nos preocupar mais em como representar melhor as necessidades de negócio em classes e comportamentos.

references:
https://pt.stackoverflow.com/questions/19548/o-que-realmente-%C3%A9-ddd-e-quando-ele-se-aplica?rq=1

O que o DDD propõe:

No DDD é proposta uma arquitetura, e boas práticas que visam:

Eliminar ou reduzir a forte dependência do core da aplicação a tecnologias, a frameworks específicos.
Reduzir o forte acoplamento entre as camadas dos sistemas, e entre os módulos.
Aumentar a reutilização de subsistemas.
Direcionar a construção de um sistema alinhado com os princípios do negócio, com uma linguagem única quando se trata de negócio, procura também aproximar as nomenclaturas, entre as equipes técnicas e os donos do negócio, procura eliminar algumas das barreiras, simplificando o entendimento entre os participantes, procura retirar maus hábitos da parte técnica, como por exemplo, utilizar nomes para objetos, métodos, variáveis e módulos com nomenclaturas próprias ou sem um bom alinhamento com o negócio.

Ubiquitous Language

A linguagem onipresente (ubiquitous Language) é a base do design direcionado por domínio (DDD).
O conceito é simples, desenvolvedores e especialistas em domínio compartilham uma linguagem comum que ambos entendem. Essa linguagem é definida na terminologia de negócios, não na terminologia técnica. Essa linguagem onipresente permite que a equipe técnica faça parte do negócio.

Domain

Agregates

Agregate (agregados) é um padrão no DDD.
Um agregate é um cluster (de Entidades ou Objetos de Valores que são encapsulados numa única classe) que pode ser tratado como uma única unidade.
Um exemplo pode ser um pedido e seus itens, esses serão objetos separados, mas é útil tratar o pedido (junto com seus itens) como um único agregate.
Um agregate terá um dos seus objetos componentes como o objeto raiz. Quaisquer referências de fora do agregate só devem se referir a raiz. A raiz pode, assim, garantir a integridade do agregate como um todo. Agregates são o elemento básico para transferência de armazenamento de dados - ao solicitar o carregamento ou ao salvar é utilizado os agregates inteiros.

As transações não devem cruzar os limites dos agregates.

references: https://martinfowler.com/bliki/DDD_Aggregate.html

Repositories

Data Access Object (DAO) manipula a incompatibilidade de impedância que um banco de dados relacional tem com técnicas orientadas a objeto.
Mas e quanto à separação de preocupações? Os DAOs estão relacionados à persistência e a persistência é infraestrutura, não domínio.
O principal problema com o uso de DAO diretamente é que temos muitas preocupações diferentes poluindo o domínio.
De acordo com o DDD, um objeto deve ser destilado até que nada permaneça que não esteja relacionado ao seu significado ou apóie seu papel nas interações.
E esse é exatamente o problema que o padrão Repositório tenta resolver.
Em DDD, você injeta repositórios, não DAOs em entidades de domínio.

Uma DAO no fim geralmente acaba possuindo diversos métodos, muitos para diferentes módulos do sistema, assim, a DAO torna-se poluída, difícil de se fazer mock, complexa ao usar testes unitários.
O repositório desvincula a DAO das regras de negócio, e fica para ela apenas o encargo de persistir dados e torná-los orientados a objetos, single responsibility.

Em resumo, um Repository:

  • Não é uma camada de acesso a dados
  • Fornece um nível mais alto de manipulação de dados
  • É ignorante quanto a como é feita a persistência
  • É uma coleção de raízes agregadas (aggregate roots)
  • Oferece um mecanismo para gerenciar entidades
  • Fala o idioma do domínio
  • Faz parte do modelo de domínio
  • Expõe uma API compatível com DDD

Muitas regras no domain model acabam sendo colocadas nas query's, consequentemente se perdendo da aplicação, os repositórios tentam fazer a transição entre o domain e a camada de persistência. Repositórios podem representar algo muito próximo aos objetos guardados na camada de persistência ou mesmo retornar cálculos, somatórias ou relatórios.

references: https://pehapkari.cz/blog/2018/02/28/domain-driven-design-repository/
https://blog.fedecarg.com/2009/03/15/domain-driven-design-the-repository/
https://thinkinginobjects.com/2012/08/26/dont-use-dao-use-repository/
https://pt.stackoverflow.com/questions/12927/qual-a-diferen%C3%A7a-entre-dao-e-repository?rq=1

Anemic Domain Model

Quando muitos dos objetos no domínio são simplemente pacotes de getters e setters, não possuem comportamento.
Há um conjunto de objetos de serviço que capturam toda a lógica do domínio, executando a regra e atualizando os objetos do modelo com os resultados. Esses serviços estão no topo do modelo de domínio e usam o modelo de domínio para os dados.
O erro fundamental desse antipadrão é que ele é contrário à ideia básica do design orientado a objetos; que é combinar dados e comportamento juntos no mesmo objeto.
O modelo de domínio anêmico é um design de estilo procedural.
Também vale enfatizar que colocar o comportamento nos objetos de domínio não vai contradizer a abordagem sólida de usar camadas para separar a lógica de domínio de coisas como persistência e responsabilidades de apresentação.
Ao colocar todo o comportamento nos serviços, você acaba criando uma aplicação baseada em scripts de transação e, assim, perde as vantagens que o modelo de domínio pode trazer.

O que geralmente ocorre nas aplicações anêmicas é a transferência do comportamento que deveria estar nos objetos de domínio para a camada de serviço (application layer).

De acordo com Eric Evans, a camada service em poucas palavras se resume como abaixo:

Application Layer (Muitas vezes chamada de Service)

  • Define as tarefas que o software deve fazer.
  • Direciona os objetos de domínio para resolver os problemas.
  • As tarefas pelas quais essa camada é responsável são significativas apenas para os negócios.
  • Ou tarefas necessárias para interação com as camadas de aplicativos de outros sistemas.
  • Esta camada é mantida fina.
  • Não contém regras de negócio ou conhecimento.
  • Mas apenas coordena tarefas e as delega para objetos de domínio na próxima camada abaixo.
  • Não tem estado refletindo a situação do negócio.
  • Mas pode ter estado que reflete o progresso de uma tarefa para o usuário ou o programa.

Domain Layer (or Model Layer):
Responsável por representar conceitos do negócio.
Possui informações sobre a situação do negócio e regras de negócios.
Tem e controla o estado que reflete a situação do negócio.
Mesmo que os detalhes técnicos de armazenamento sejam delegados à infraestrutura.
Essa camada é o coração do software.

references: https://martinfowler.com/bliki/AnemicDomainModel.html

View - Application - Domain - Infrastructure

Objetos de Domínio Anêmicos

O excesso de uso de getters e setters quebram alguns dos princípios da orientação a objetos, como encapsulamento.
Quando um objeto tem suas propriedades protegidas, e comportamento interno independente, podemos alterar tanto os atributos privados como os a implementação dos métodos que isso não necessariamente afetaria as aplicações que o usam diretamente, mas com objetos anêmicos em que os getters e setters são simples expositores de atributos isso se torna praticamente impossível.
Ou seja, se você altera uma implementação de um método do objeto, você não tem como garantir que isso irá afetar os serviços que o usam, na verdade geralmente irá afetar, pois, há um forte acoplamento.
Mas com um forte encapsulamento, há uma clara e bem definida fronteira entre o que são métodos internos, atributos, e o que é exposto externo, propriedades e métodos públicos, o que se resume nas interfaces.

referências: http://www.drdobbs.com/windows/saying-no-to-properties/240005920?pgno=2

Separação de Objetos de Domínio de Entidades, VOs e DTOs

Considerando um atributo id.
Para que um implementar um setID na sua chave primária se o seu framework vai utilizar reflection ou manipulação de bytecode para pegar o atributo privado? Ou se você pode passá-lo pelo construtor? Quando se trata de value objects, você realmente precisa dos seus setters?

Em muitos casos VOs são criados apenas para expor os dados, e não há necessidade alguma para os setters… bastando um bom construtor! Dessa forma evitamos um modelo de domínio anêmico.

Para objetos que não possuem encapsulamento por natureza, não faz sentido seguir certos conceitos OO.
Chega a ser contraproducente implementar um cálculo dentro de um getter. Em uma aplicação robusta, quando numa manutenção, ao procurar o motivo de algum dado inconsistente, o último lugar em que você vai procurar uma regra de negócio é dentro de um getter.

Então, como você elimina o get / sets? A chave principal é repensar a abordagem dos objetos. Um objeto deve ser definido pelo que ele faz, não pelo que ele contém. Na verdade, você não deve saber o que o objeto contém.

referências: https://blog.caelum.com.br/nao-aprender-oo-getters-e-setters/, http://www.drdobbs.com/windows/saying-no-to-properties/240005920?pgno=3

Entities

Muitos objetos não são fundamentalmente definidos por seus atributos, mas por uma sequência e por identidade.

VO

preferencialmente imutável.
não contém identidade (identity).
dois objetos com referencias de memória diferentes, mas com valores iguais são considerados iguais.
geralmente não será necessário getters e setters (não são utilizados pelos frameworks).
não deve conter nenhuma regra de negócio.

DTO

para transferência entre camadas, com o objetivo de reduzir as requisições, transferindo diveras informações.
geralmente serializável.
utilizado para entregar dados filtrados para uma determinada camada, por exemplo, a senha, o endereço do cliente não precisam ser transferidos para algumas telas.
não deve conter nenhuma regra de negócio.
O DTO ajuda no transporte dos atributos da camada de visualização para a camada de negócios e, finalmente, para a camada de persistência e vice-versa.

Factories

When creation of an object, or an entire AGGREGATE, becomes complicated or reveals too much of the internal structure, FACTORIES provide encapsulation" O livro cita as mesmas fábricas do Design Patterns do GOF, Factory method, abstract factory, builder - Gamma et al.1995. Um detalhe importante é, pensando na divisão das camadas da aplicação, onde colocar suas factories? O lugar correto segundo DDD é na própria domain layer: "Creation of an object can be a major operation in itself, but complex assembly operations do not fit the responsibility of the created objects. Complex object creation is a responsability of the domain layer" "We are adding elements to the design that do not correspond to anything in the model, but they are nonetheless part of the domain's layer responsability

references: http://andersonleite.com.br/2009/04/03/ddd-part-5-factories-e-repositories.html

Modules

Módulos – abstrações que têm por objetivos agrupar classes por um determinado conceito do domínio.
A maioria das linguagens de programação oferecem suporte a módulos (pacotes em Java, namespaces em .NET ou módulos em Ruby).
Um anti-padrão bem comum é a criação de módulos que agrupam as classes segundo conceitos de infra-estrutura.
Um exemplo seria, ao se trabalhar com Struts, em Java, criar um pacote que conterá todas as Actions do sistema.

Ao usar DDD devemos agrupar classes se esse agrupamento faz sentido do ponto de vista do domínio, ou seja, do negócio.

Se tivermos, por exemplo, várias classes que compõem informações de Paciente num sistema médico, podemos criar um módulo chamado paciente e colocar classes como Ficha, PrescricaoMedica, RegistroDeConsulta e HistoricoDeCirurgias num mesmo pacote.

MDD

TDD

Translation Maps

Produtor - Consumidor Conformista Núcleo Compartilhado

Camada Anti-Corrupção

Favorecer Reutilização

Código alinhado com o negócio

Mínimo de acoplamento

Independência de tecnologia

Modelo de domínio

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment