Estoy decidiendo si debo usar un modelo de dominio rico en lugar de un modelo de dominio anémico, y estoy buscando buenos ejemplos de los dos.
He estado construyendo aplicaciones web usando un modelo de dominio anémico, respaldado por un servicio -> repositorio -> sistema de capa de almacenamiento , usando FluentValidation para la validación de BL y poniendo todo mi BL en la capa de servicio.
He leído el libro DDD de Eric Evan, y él (junto con Fowler y otros) parece pensar que los modelos de dominio anémico son un anti-patrón.
Así que realmente quería tener una idea de este problema.
Además, realmente estoy buscando algunos buenos ejemplos (básicos) de un modelo de dominio enriquecido y los beneficios que ofrece sobre el modelo de dominio anémico.
Respuestas:
La diferencia es que un modelo anémico separa la lógica de los datos. La lógica se colocan a menudo en las clases nombradas
**Service
,**Util
,**Manager
,**Helper
y así sucesivamente. Estas clases implementan la lógica de interpretación de datos y, por lo tanto, toman el modelo de datos como argumento. P.ejmientras que el enfoque de dominio rico invierte esto al colocar la lógica de interpretación de datos en el modelo de dominio rico. Por lo tanto, junta la lógica y los datos y un modelo de dominio rico se vería así:
Esto tiene un gran impacto en la coherencia del objeto. Dado que la lógica de interpretación de datos envuelve los datos (solo se puede acceder a los datos a través de métodos de objeto), los métodos pueden reaccionar a los cambios de estado de otros datos -> Esto es lo que llamamos comportamiento.
En un modelo anémico, los modelos de datos no pueden garantizar que estén en un estado legal, mientras que en un modelo de dominio rico sí pueden. Un modelo de dominio rico aplica principios de OO como encapsulación, ocultación de información y unir datos y lógica y, por lo tanto, un modelo anémico es un anti patrón desde la perspectiva de OO.
Para una visión más profunda, eche un vistazo a mi blog https://www.link-intersystems.com/blog/2011/10/01/anemic-vs-rich-domain-models/
fuente
Bozhidar Bozhanov parece argumentar a favor del modelo anémico en esta publicación de blog.
Aquí está el resumen que presenta:
Los objetos de dominio no deben administrarse mediante Spring (IoC), no deben tener DAO ni nada relacionado con la infraestructura inyectada en ellos
los objetos de dominio tienen los objetos de dominio de los que dependen establecidos por hibernación (o el mecanismo de persistencia)
Los objetos de dominio realizan la lógica empresarial, como es la idea central de DDD, pero esto no incluye consultas de base de datos o CRUD, solo operaciones en el estado interno del objeto
Rara vez se necesitan DTO: los objetos de dominio son los DTO en sí mismos en la mayoría de los casos (lo que ahorra algo de código repetitivo)
Los servicios realizan operaciones CRUD, envían correos electrónicos, coordinan los objetos del dominio, generan informes basados en múltiples objetos del dominio, ejecutan consultas, etc.
la capa de servicio (aplicación) no es tan delgada, pero no incluye reglas comerciales que son intrínsecas a los objetos de dominio
Debe evitarse la generación de código. La abstracción, los patrones de diseño y DI deben usarse para superar la necesidad de generación de código y, en última instancia, para deshacerse de la duplicación de código.
ACTUALIZAR
Recientemente leí este artículo donde el autor aboga por seguir una especie de enfoque híbrido: los objetos de dominio pueden responder varias preguntas basándose únicamente en su estado (que en el caso de modelos totalmente anémicos probablemente se haría en la capa de servicio)
fuente
Mi punto de vista es este:
Modelo de dominio anémico = tablas de base de datos asignadas a objetos (solo valores de campo, sin comportamiento real)
Modelo de dominio rico = una colección de objetos que exponen el comportamiento
Si desea crear una aplicación CRUD simple, tal vez un modelo anémico con un marco MVC clásico sea suficiente. Pero si desea implementar algún tipo de lógica, modelo anémico significa que no hará programación orientada a objetos.
* Tenga en cuenta que el comportamiento de los objetos no tiene nada que ver con la persistencia. Una capa diferente (Data Mappers, Repositories, etc.) es responsable de la persistencia de los objetos de dominio.
fuente
x
,y
,sum
ydifference
. Son cuatro cosas. O podría argumentar que es suma y resta (dos cosas). O podrías argumentar que son matemáticas (una cosa). Hay muchas publicaciones de blog sobre cómo encontrar un equilibrio al aplicar SRP. Aquí hay uno: hackernoon.com/…La Figura 1 muestra un modelo de dominio anémico, que es básicamente un esquema con captadores y definidores.
En este modelo más rico, en lugar de simplemente exponer propiedades para ser leídas y escritas, la superficie pública de Cliente se compone de métodos explícitos.
fuente
Address
, sinoExtendedAddress
heredarAddress
, con varias propiedades adicionales? 2) ¿O cambiarCustomerCreditCard
los parámetros del constructor para tomar enBankID
lugar deBankName
?Uno de los beneficios de las clases de dominios enriquecidos es que puede llamar a su comportamiento (métodos) cada vez que tenga la referencia al objeto en cualquier capa. Además, tiende a escribir métodos pequeños y distribuidos que colaboran juntos. En las clases de dominio anémicas, tiende a escribir métodos de procedimiento gruesos (en la capa de servicio) que generalmente son impulsados por casos de uso. Por lo general, son menos fáciles de mantener en comparación con las clases de dominio enriquecidas.
Un ejemplo de clases de dominio con comportamientos:
El método
needToDeliver()
devolverá la lista de artículos que deben entregarse, incluida la bonificación. Se puede llamar dentro de la clase, desde otra clase relacionada o desde otra capa. Por ejemplo, si pasaOrder
a ver, puede usarneedToDeliver()
de seleccionadoOrder
para mostrar la lista de elementos que debe confirmar el usuario antes de hacer clic en el botón Guardar para conservar el archivoOrder
.Responder a un comentario
Así es como uso la clase de dominio del controlador:
La creación de
Order
yLineItem
es en una sola transacción. Si uno de losLineItem
no se puede crear, noOrder
se creará.Tiendo a tener métodos que representan una sola transacción, como:
Todo
deliver()
lo que esté dentro se ejecutará como una sola transacción. Si necesito ejecutar muchos métodos no relacionados en una sola transacción, crearía una clase de servicio.Para evitar la excepción de carga diferida, utilizo el gráfico de entidad con nombre JPA 2.1. Por ejemplo, en el controlador para la pantalla de entrega, puedo crear un método para cargar
delivery
atributos e ignorarlosbonus
, comorepository.findOrderByNumberFetchDelivery()
. En la pantalla de bonificación, llamo a otro método que carga elbonus
atributo e ignoradelivery
, comorepository.findOrderByNumberFetchBonus()
. Esto requiere disciplina ya que todavía no puedo llamardeliver()
dentro de la pantalla de bonificación.fuente
Cuando solía escribir aplicaciones de escritorio monolíticas, construía modelos de dominio ricos, solía disfrutar construyéndolos.
Ahora escribo microservicios HTTP diminutos, hay la menor cantidad de código posible, incluidos los DTO anémicos.
Creo que DDD y este argumento anémico datan de la era monolítica de las aplicaciones de escritorio o servidor. Recuerdo esa época y estaría de acuerdo en que los modelos anémicos son extraños. Construí una gran aplicación de comercio de divisas monolítica y no había ningún modelo, de verdad, fue horrible.
Con los microservicios, los pequeños servicios con su rico comportamiento son posiblemente los modelos componibles y agregados dentro de un dominio. Por lo tanto, es posible que las implementaciones de microservicios en sí mismas no requieran más DDD. La aplicación de microservicio puede ser el dominio.
Un microservicio de pedidos puede tener muy pocas funciones, expresadas como recursos RESTful o vía SOAP o lo que sea. El código de microservicio de pedidos puede ser extremadamente simple.
Un servicio (micro) único más grande y monolítico, especialmente uno que mantiene su modelo en RAM, puede beneficiarse de DDD.
fuente
Creo que la raíz del problema está en una falsa dicotomía. ¿Cómo es posible extraer estos 2 modelos: rico y "anémico" y contrastarlos entre sí? Creo que solo es posible si tienes ideas equivocadas sobre lo que es una clase . No estoy seguro, pero creo que lo encontré en uno de los videos de Bozhidar Bozhanov en Youtube. Una clase no es un método de datos + sobre estos datos. Es una comprensión totalmente inválida que lleva a la división de clases en dos categorías: solo datos, modelo tan anémico y métodos + datos - tan rico modelo (para ser más correctos, hay una tercera categoría: solo métodos pares).
Lo cierto es que clase es un concepto en algún modelo ontológico, una palabra, una definición, un término, una idea, es un DENOTAT . Y esta comprensión elimina la falsa dicotomía: no se puede tener SOLO modelo anémico o SOLO modelo rico, porque significa que su modelo no es adecuado, no es relevante para la realidad: algunos conceptos solo tienen datos, algunos solo tienen métodos, algunos de ellos se mezclan. Porque tratamos de describir, en este caso, algunas categorías, conjuntos de objetos, relaciones, conceptos con clases, y como sabemos, algunos conceptos son solo procesos (métodos), algunos de ellos son solo conjuntos de atributos (datos), algunos de son relaciones con atributos (mixtos).
Creo que una aplicación adecuada debería incluir todo tipo de clases y evitar fanáticamente autolimitarse a un solo modelo. No importa cómo se represente la lógica: con código o con objetos de datos interpretables (como Free Monads ), de todos modos: deberíamos tener clases (conceptos, denotat) que representen procesos, lógica, relaciones, atributos, características, datos, etc. y no para tratar de evitar algunos de ellos o reducirlos a un solo tipo.
Entonces, podemos extraer la lógica a otra clase y dejar los datos en la original, pero no tiene sentido porque algún concepto puede incluir atributos y relaciones / procesos / métodos y una separación de ellos duplicará el concepto bajo 2 nombres que pueden ser reducido a patrones: "OBJETO-Atributos" y "OBJETO-Lógica". Está bien en los lenguajes procedimentales y funcionales debido a su limitación, pero es un excesivo autocontrol para un lenguaje que te permite describir todo tipo de conceptos.
fuente
Los modelos de dominio anémicos son importantes para ORM y su fácil transferencia a través de redes (la sangre vital de todas las aplicaciones comerciales), pero OO es muy importante para encapsular y simplificar las partes 'transaccionales / de manejo' de su código.
Por eso lo importante es poder identificarse y convertir de un mundo a otro.
Nombre modelos Anemic algo como AnemicUser o UserDAO, etc. para que los desarrolladores sepan que hay una mejor clase para usar, luego tenga un constructor apropiado para la clase none Anemic
y método adaptador para crear la clase anémica para transporte / persistencia
Trate de usar el usuario no anémico en todas partes fuera del transporte / persistencia
fuente
Aquí hay un ejemplo que podría ayudar:
Anémico
No anémico
fuente
El enfoque clásico de DDD no dice evitar a toda costa los modelos anémicos y ricos. Sin embargo, MDA aún puede aplicar todos los conceptos de DDD (contextos limitados, mapas de contexto, objetos de valor, etc.) pero usa modelos Anemic vs Rich en todos los casos. Hay muchos casos en los que el uso de servicios de dominio para orquestar casos de uso de dominio complejos en un conjunto de agregados de dominio es un enfoque mucho mejor que simplemente los agregados que se invocan desde la capa de aplicación. La única diferencia con el enfoque clásico de DDD es ¿dónde residen todas las validaciones y reglas comerciales? Hay una nueva construcción conocida como validadores de modelos. Los validadores garantizan la integridad del modelo de entrada completo antes de que se lleve a cabo cualquier caso de uso o flujo de trabajo de dominio. Las entidades raíz e hijo agregadas son anémicas, pero cada una puede tener sus propios validadores de modelo invocados según sea necesario, por su validador raíz. Los validadores aún se adhieren a SRP, son fáciles de mantener y se pueden probar por unidades.
La razón de este cambio es que ahora nos estamos moviendo más hacia una API en lugar de un primer enfoque de UX para los microservicios. REST ha jugado un papel muy importante en esto. El enfoque de API tradicional (debido a SOAP) se fijó inicialmente en una API basada en comandos frente a los verbos HTTP (POST, PUT, PATCH, GET y DELETE). Una API basada en comandos encaja bien con el enfoque orientado a objetos del modelo enriquecido y sigue siendo muy válida. Sin embargo, las API basadas en CRUD simples, aunque pueden encajar dentro de un modelo enriquecido, se adaptan mucho mejor a modelos anémicos simples, validadores y servicios de dominio para orquestar el resto.
Me encanta DDD en todo lo que tiene que ofrecer, pero llega un momento en que necesitas estirarlo un poco para adaptarse a un enfoque de arquitectura en constante cambio y mejor.
fuente