Estou decidindo se devo usar um Modelo de Domínio Rico em vez de um Modelo de Domínio Anêmico e estou procurando bons exemplos dos dois.
Tenho construído aplicações web usando um Modelo de Domínio Anêmico, apoiado por um Serviço -> Repositório -> Sistema de camada de armazenamento , usando FluentValidation para validação de BL, e colocando todo meu BL na camada de Serviço.
Eu li o livro DDD de Eric Evan, e ele (junto com Fowler e outros) parece pensar que Modelos de Domínio Anêmico são um antipadrão.
Então, eu só queria saber mais sobre esse problema.
Além disso, estou realmente procurando alguns bons exemplos (básicos) de um Modelo de Domínio Rico e os benefícios sobre o Modelo de Domínio Anêmico que ele oferece.
Respostas:
A diferença é que um modelo anêmico separa a lógica dos dados. A lógica é muitas vezes colocados em classes nomeadas
**Service
,**Util
,**Manager
,**Helper
e assim por diante. Essas classes implementam a lógica de interpretação de dados e, portanto, usam o modelo de dados como argumento. Por exemploenquanto a abordagem de domínio rico inverte isso, colocando a lógica de interpretação de dados no modelo de domínio rico. Assim, ele reúne lógica e dados e um modelo de domínio rico ficaria assim:
Isso tem um grande impacto na consistência do objeto. Visto que a lógica de interpretação de dados envolve os dados (os dados só podem ser acessados por meio de métodos de objeto), os métodos podem reagir às mudanças de estado de outros dados -> Isso é o que chamamos de comportamento.
Em um modelo anêmico, os modelos de dados não podem garantir que estejam em um estado legal, enquanto em um modelo de domínio rico podem. Um modelo de domínio rico aplica princípios OO como encapsulamento, ocultação de informações e junção de dados e lógica e, portanto, um modelo anêmico é um antipadrão de uma perspectiva OO.
Para uma visão mais aprofundada, dê uma olhada em meu blog https://www.link-intersystems.com/blog/2011/10/01/anemic-vs-rich-domain-models/
fonte
Bozhidar Bozhanov parece argumentar a favor do modelo anêmico nesta postagem do blog.
Aqui está o resumo que ele apresenta:
objetos de domínio não devem ser gerenciados por Spring (IoC), eles não devem ter DAOs ou qualquer coisa relacionada à infraestrutura injetada neles
objetos de domínio têm os objetos de domínio dos quais dependem definidos pelo hibernate (ou mecanismo de persistência)
objetos de domínio executam a lógica de negócios, como é a ideia central do DDD, mas isso não inclui consultas de banco de dados ou operações CRUD - apenas no estado interno do objeto
raramente há necessidade de DTOs - os objetos de domínio são os próprios DTOs na maioria dos casos (o que salva algum código clichê)
os serviços realizam operações CRUD, enviam e-mails, coordenam os objetos do domínio, geram relatórios com base em vários objetos do domínio, executam consultas, etc.
a camada de serviço (aplicativo) não é tão fina, mas não inclui regras de negócios que são intrínsecas aos objetos de domínio
a geração de código deve ser evitada. Abstração, padrões de projeto e DI devem ser usados para superar a necessidade de geração de código e, em última análise, para se livrar da duplicação de código.
ATUALIZAR
Recentemente li este artigo em que o autor defende seguir uma espécie de abordagem híbrida - objetos de domínio podem responder a várias perguntas com base apenas em seu estado (o que, no caso de modelos totalmente anêmicos, provavelmente seria feito na camada de serviço)
fonte
Meu ponto de vista é este:
Modelo de domínio anêmico = tabelas de banco de dados mapeadas para objetos (apenas valores de campo, nenhum comportamento real)
Modelo de domínio rico = uma coleção de objetos que expõe o comportamento
Se você deseja criar um aplicativo CRUD simples, talvez um modelo anêmico com um framework MVC clássico seja o suficiente. Mas se você deseja implementar algum tipo de lógica, o modelo anêmico significa que você não fará programação orientada a objetos.
* Observe que o comportamento do objeto não tem nada a ver com persistência. Uma camada diferente (mapeadores de dados, repositórios etc.) é responsável por persistir os objetos de domínio.
fonte
x
,y
,sum
edifference
. São quatro coisas. Ou você pode argumentar que é adição e subtração (duas coisas). Ou você pode argumentar que é matemática (uma coisa). Existem muitos posts por aí sobre como encontrar um equilíbrio na aplicação do SRP. Aqui está um: hackernoon.com/…A Figura 1 mostra um Modelo de Domínio Anêmico, que é basicamente um esquema com getters e setters.
Nesse modelo mais rico, em vez de simplesmente expor propriedades para serem lidas e gravadas, a superfície pública do Cliente é composta de métodos explícitos.
fonte
Address
, masExtendedAddress
herdar deAddress
, várias propriedades adicionais? 2) Ou altereCustomerCreditCard
os parâmetros do construtor para assumir emBankID
vez deBankName
?Um dos benefícios das classes de domínio ricas é que você pode chamar seu comportamento (métodos) sempre que tiver a referência ao objeto em qualquer camada. Além disso, você tende a escrever métodos pequenos e distribuídos que colaboram juntos. Em classes de domínio anêmicas, você tende a escrever métodos processuais gordos (na camada de serviço) que geralmente são orientados pelo caso de uso. Eles geralmente são menos fáceis de manter em comparação com as classes de domínio avançadas.
Um exemplo de classes de domínio com comportamentos:
O método
needToDeliver()
retornará uma lista de itens que precisam ser entregues, incluindo bônus. Ele pode ser chamado dentro da classe, de outra classe relacionada ou de outra camada. Por exemplo, se você passarOrder
para visualizar, poderá usarneedToDeliver()
de selecionadoOrder
para exibir a lista de itens a serem confirmados pelo usuário antes de clicar no botão Salvar para persistir oOrder
.Respondendo a um comentário
É assim que eu uso a classe de domínio do controlador:
A criação de
Order
eLineItem
é em uma transação. Se um dosLineItem
não puder ser criado, nenhumOrder
será criado.Costumo ter métodos que representam uma única transação, como:
Qualquer coisa dentro
deliver()
será executada como uma única transação. Se eu precisar executar muitos métodos não relacionados em uma única transação, criaria uma classe de serviço.Para evitar a exceção de carregamento lento, eu uso o gráfico de entidade nomeada JPA 2.1. Por exemplo, no controlador para tela de entrega, posso criar um método para carregar o
delivery
atributo e ignorarbonus
, comorepository.findOrderByNumberFetchDelivery()
. Na tela de bônus, chamo outro método que carrega obonus
atributo e ignorodelivery
, comorepository.findOrderByNumberFetchBonus()
. Isso requer disciplina, pois ainda não consigo ligardeliver()
para a tela de bônus.fonte
Quando eu costumava escrever aplicativos de desktop monolíticos, construí modelos de domínio ricos, costumava gostar de criá-los.
Agora eu escrevo microsserviços HTTP minúsculos, há o mínimo de código possível, incluindo DTOs anêmicos.
Acho que o DDD e esse argumento anêmico datam da era monolítica de desktop ou aplicativo de servidor. Lembro-me dessa época e concordo que os modelos anêmicos são estranhos. Eu construí um grande aplicativo de negociação FX monolítico e não havia nenhum modelo, realmente, era horrível.
Com microsserviços, os pequenos serviços com seu rico comportamento, são indiscutivelmente os modelos combináveis e agregados dentro de um domínio. Portanto, as próprias implementações de microsserviço podem não exigir mais DDD. O aplicativo de microsserviço pode ser o domínio.
Um microsserviço de pedidos pode ter muito poucas funções, expressas como recursos RESTful ou via SOAP ou qualquer outra coisa. O código do microsserviço de pedidos pode ser extremamente simples.
Um serviço único (micro) único mais monolítico maior, especialmente aquele que o mantém na memória RAM, pode se beneficiar do DDD.
fonte
Acho que a raiz do problema está na falsa dicotomia. Como é possível extrair esses 2 modelos: ricos e "anêmicos" e contrastá-los? Acho que só é possível se você tiver ideias erradas sobre o que é uma classe . Não tenho certeza, mas acho que encontrei em um dos vídeos de Bozhidar Bozhanov no Youtube. Uma classe não é um dados + métodos sobre esses dados. É um entendimento totalmente inválido que leva à divisão das classes em duas categorias: somente dados, modelo tão anêmico e dados + métodos - modelo tão rico (para ser mais correto, há uma 3ª categoria: métodos apenas pares).
A verdade é que classe é um conceito em algum modelo ontológico, uma palavra, uma definição, um termo, uma ideia, é um DENOTAT . E esse entendimento elimina a falsa dicotomia: você não pode ter APENAS modelo anêmico ou APENAS modelo rico, pois significa que seu modelo não é adequado, não é relevante para a realidade: alguns conceitos possuem apenas dados, alguns deles possuem apenas métodos, alguns deles são misturados. Porque tentamos descrever, neste caso, algumas categorias, conjuntos de objetos, relações, conceitos com classes, e como sabemos, alguns conceitos são apenas processos (métodos), alguns deles são apenas conjuntos de atributos (dados), alguns de eles são relações com atributos (mistos).
Acho que uma aplicação adequada deve incluir todos os tipos de classes e evitar que se autolimite fanaticamente a apenas um modelo. Não importa como a lógica está representando: com código ou com objetos de dados interpretáveis (como Mônadas Livres ), de qualquer maneira: devemos ter classes (conceitos, denotats) representando processos, lógica, relações, atributos, recursos, dados, etc. e não tentar evitar alguns deles ou reduzir todos eles a um único tipo.
Assim, podemos extrair lógica para outra classe e deixar os dados na classe original, mas não faz sentido porque alguns conceitos podem incluir atributos e relações / processos / métodos e uma separação deles duplicará o conceito sob 2 nomes que podem ser reduzido a padrões: "OBJECT-Attributes" e "OBJECT-Logic". É bom em linguagens procedurais e funcionais por causa de suas limitações, mas é autocontenção excessiva para uma linguagem que permite a você descrever todos os tipos de conceitos.
fonte
Modelos de domínio anêmico são importantes para ORM e fácil transferência através de redes (o sangue vital de todas as aplicações comerciais), mas OO é muito importante para encapsular e simplificar as partes 'transacionais / manuseio' de seu código.
Portanto, o importante é ser capaz de se identificar e converter de um mundo para o outro.
Nomeie modelos anêmicos como AnemicUser ou UserDAO etc. para que os desenvolvedores saibam que há uma classe melhor para usar e, em seguida, tenham um construtor apropriado para a classe não anêmica
e o método do adaptador para criar a classe anêmica para transporte / persistência
Objetivo de usar o usuário não anêmico em qualquer lugar fora do transporte / persistência
fonte
Aqui está um exemplo que pode ajudar:
Anêmico
Não anêmico
fonte
A abordagem clássica para DDD não afirma evitar modelos anêmicos versus modelos ricos a todo custo. No entanto, o MDA ainda pode aplicar todos os conceitos DDD (contextos limitados, mapas de contexto, objetos de valor etc.), mas usar modelos anêmicos versus ricos em todos os casos. Há muitos casos em que o uso de Serviços de Domínio para orquestrar Casos de Uso de Domínio complexos em um conjunto de agregados de domínio é uma abordagem muito melhor do que apenas agregados sendo chamados da camada de aplicativo. A única diferença da abordagem DDD clássica é onde todas as validações e regras de negócios residem? Há uma nova construção conhecida como validadores de modelo. Os validadores garantem a integridade de todo o modelo de entrada antes que qualquer caso de uso ou fluxo de trabalho de domínio ocorra. A raiz agregada e as entidades filhas são anêmicas, mas cada uma pode ter seus próprios validadores de modelo chamados conforme necessário, por seu validador de raiz. Os validadores ainda aderem ao SRP, são fáceis de manter e podem ser testados em unidades.
O motivo dessa mudança é que agora estamos avançando mais em direção a uma API primeiro em vez de uma abordagem UX inicial para microsserviços. REST desempenhou um papel muito importante nisso. A abordagem tradicional da API (por causa do SOAP) foi inicialmente fixada em uma API baseada em comandos versus verbos HTTP (POST, PUT, PATCH, GET e DELETE). Uma API baseada em comandos se encaixa bem com a abordagem orientada a objetos Rich Model e ainda é muito válida. No entanto, APIs simples baseadas em CRUD, embora possam caber em um Modelo Rico, são muito mais adequadas com modelos anêmicos simples, validadores e Serviços de Domínio para orquestrar o resto.
Eu amo o DDD em tudo o que ele tem a oferecer, mas chega um momento em que você precisa esticá-lo um pouco para se adequar a mudanças constantes e uma abordagem melhor à arquitetura.
fonte