language-agnostic design-patterns

language agnostic - Modelo de dominio anémico: Pros/Contras



language-agnostic design-patterns (16)

"Es un antipatrón, por lo que otros desarrolladores te preguntarán si entiendes los conceptos de diseño orientado a objetos".

"Un modelo de dominio anémico es un antipatrón. Los antipatrones no tienen pros".

Si el modelo de dominio anémico es un antipatrón es una cuestión de opinión. Martin Fowler dice que sí, que una serie de desarrolladores que saben de cerca OO dicen que no es así. Declarar la opinión como un hecho rara vez es útil.

Incluso si se aceptara universalmente que fuera un antipatrón, es probable que todavía tenga algún aspecto positivo (aunque relativamente poco).

Me gustaría saber cuáles son los pros y los contras para usar un Modelo de dominio Anemic (ver el enlace a continuación).

Artículo de Fowler


"Los desarrolladores trabajan para poner una" cara "similar a un servicio en un sistema heredado diseñado en una forma no OO (independientemente del idioma)".

Si piensa en muchas aplicaciones LOB, estos sistemas heredados a menudo no usarán el mismo modelo de dominio que usted. El Modelo de Dominio Anemic resuelve esto con el uso de la lógica de negocios en clases de servicio. Podrías poner todo este código de interfaz dentro de tu modelo (en el sentido tradicional de OO), pero normalmente terminas perdiendo modularidad.


Cabe señalar que a medida que los sistemas crecen en complejidad y granularidad de variación, la encapsulación y consolidación de los puntos de interfaz proporcionados por un modelo de objeto de paso de mensajes bien diseñado hace que sea mucho más seguro cambiar y mantener el código crítico sin una refacturación generalizada.

Las capas de servicio creadas por el ADM, aunque ciertamente más fáciles de implementar (ya que requieren relativamente poca atención y tienen muchos puntos de interfaz descentralizados) probablemente crearán problemas en el futuro cuando sea el momento de modificar un sistema en vivo y en crecimiento.

También podría agregar que no todos los casos requieren un modelo de dominio (y mucho menos el de ADM). A veces es mejor usar un estilo de trabajo más funcional / de procedimiento que se basa en datos y no depende de las reglas lógicas / comerciales de toda la aplicación.

Si está tratando de decidir sobre los pros y los contras de una aplicación completa, creo que es importante primero diseñar cómo se verá cada uno de ellos para su aplicación ANTES incluso de comenzar a escribir una sola línea de código. Una vez que haya seleccionado CRC o enmarcado su aplicación en ambos estilos, dé un paso atrás y decida cuál tiene más sentido y se adapta mejor a la aplicación.

También piense por adelantado cuál será más fácil de mantener ...


Con el "Modelo de dominio anémico" como antipatrón, ¿por qué hay tantos sistemas que implementan esto?

Creo que hay varias razones

1. Complejidad del sistema

En un sistema simple (que es casi todos los ejemplos y código de muestra que encuentras en Internet) si quiero implementar:

Agregar producto al pedido

Puse esta función en la orden

public void Order.AddOrderLine(Product product) { OrderLines.Add(new OrderLine(product)); }

Agradable y súper orientado a objetos.

Ahora digamos que necesito asegurarme de que necesito validar que el producto existe en el inventario y lanzar una excepción si no lo hace.

No puedo ponerlo en orden por más tiempo, ya que no quiero que mi pedido dependa de inventario, por lo que ahora debe ir al servicio

public void OrderService.AddOrderLine(Order order, Product product) { if (!InventoryService.Has(product) throw new AddProductException order.AddOrderLine(product); }

También podría pasar IInventoryService a Order.AddOrderLine, que es otra opción, pero que aún hace que Order dependa de InventoryService.

Todavía hay alguna funcionalidad en Order.AddOrderLine, pero generalmente está limitada al alcance del pedido, mientras que en mi experiencia hay mucho más ámbito fuera de servicio de Business Logic.

Cuando el sistema es más que simplemente CRUD básico, terminará con la mayor parte de su lógica en OrderService y muy poco en Order.

2. Vista del desarrollador de OOP

Hay muchas discusiones acaloradas en Internet acerca de qué lógica deberían aplicarse a las entidades.

Algo como

Order.Save

¿Debería Order saber cómo salvarse o no? Digamos que tenemos repositorios para eso.

Ahora puede ordenar agregar líneas de orden? Si trato de darle sentido utilizando un inglés simple, tampoco tiene sentido. El usuario agrega el Producto al pedido, entonces ¿deberíamos hacer User.AddOrderLineToOrder ()? Eso parece exagerado.

¿Qué tal OrderService.AddOrderLine ()? ¡Ahora tiene sentido!

Mi comprensión de OOP es que para la encapsulación se colocan funciones en las clases donde la función necesitará acceder al estado interno de la clase. Si necesito acceder a la colección Order.OrderLines, puse Order.AddOrderLine () en Order. De esta manera, el estado interno de la clase no se expone.

3. Contenedores de IoC

Los sistemas que usan contenedores IoC generalmente son completamente anémicos.

Es porque puede probar sus servicios / repositorios que tienen interfaces, pero no puede probar objetos de dominio (fácilmente), a menos que ponga interfaces en todos ellos.

Dado que actualmente se elogia a "IoC" como solución para todos sus problemas de programación, mucha gente lo sigue ciegamente y de esta forma termina con los modelos de dominio anémico.

4. OOP es difícil, el procedimiento es fácil

Tengo un poco de " Curse of Knowledge " en este caso, pero he descubierto que para los desarrolladores más nuevos tener DTO y servicios es mucho más fácil que Rich Domain.

Posiblemente se deba a que con Rich Domain es más difícil saber en qué clases poner la lógica. Cuándo crear nuevas clases? ¿Qué patrones usar? etc.

Con los servicios apátridas, solo póngalo en el servicio con el nombre más cercano.


Cuando me encontré por primera vez con el artículo de Anemic Domain Model, pensé "Holy s ***, eso es lo que hago. ¡Horror!" Perseveré y seguí las referencias al libro de Eric Evan, considerado como un buen ejemplo, y descargué la fuente. Resulta que "no usar un modelo de dominio anémico" no significa "no usar clases de servicio, no usar mediadores, no usar estrategias" o incluso "poner la lógica en la clase manipulada".

Los ejemplos de DDD tienen clases de servicio, XyzUpdaters, singletons e IoC.

Sigo confundido por lo que exactamente es un Modelo de Dominio Anemico. Espero "Lo sabré cuando lo vea". Por ahora estoy contento con un ejemplo positivo de buen diseño.


Da una mejor predictibilidad. A los gerentes les gusta eso, especialmente si el proyecto cuenta con tiempo y materiales pagados. Cada cambio significa mucho trabajo, por lo que el trabajo difícil puede ocultarse detrás de un montón de trabajo repetitivo. En un sistema DRY bien diseñado, la previsibilidad es muy mala, ya que constantemente estás haciendo cosas nuevas.


Después de esto, ha habido un pensamiento en mi cabeza desde hace mucho tiempo. Creo que el término "OOP" ha adquirido un significado que no está realmente destinado a él. El anagrama significa "programación orientada a objetos", como todos bien sabemos. El enfoque del curso está en la palabra "orientado". No es "OMP", que significa "programación ordenada por objeto". Tanto ADM como RDM son ejemplos de OOP. Hacen uso de objetos, propiedades, interfaces de métodos, etc. Sin embargo, existe una diferencia entre ADM y RDM en cómo elegimos encapsular cosas. Son dos cosas diferentes. Decir que ADM es malo OOP no es una declaración precisa. Tal vez necesitemos diferentes términos para varios niveles de encapsulación en su lugar. Además, nunca me gustó el término anti-patrón. Por lo general, los miembros de un grupo oponente lo asignan a algo. Tanto ADM como RDM son patrones válidos, simplemente tienen diferentes objetivos en mente y están destinados a resolver las diferentes necesidades comerciales. Aquellos de nosotros que practicamos DDD deberíamos, al menos, apreciar esto y no caer al nivel de los demás al criticar a los que eligen implementar ADM. Solo mis pensamientos.


Después de haber trabajado con un sistema "maduro" con un ADM, siento que puedo brindar al menos alguna retroalimentación anecdótica a esta pregunta.

1) Falta de encapsulación

En un sistema en vivo con un ADM existe la posibilidad de escribir, por ejemplo, ''obj.x = 100; obj.save '', incluso si esto viola la lógica comercial. Esto conduce a una serie de errores que no se encontrarían si los invariantes se modelaron en el objeto. Es la severidad y la omnipresencia de estos errores que siento que es la más seria negativa para un ADM.

Creo que es importante señalar aquí que aquí es donde una solución funcional, y las soluciones de procedimiento de un ADM difieren significativamente y cualquier similitud que otros puedan haber dibujado a la superficie similitudes entre un ADM y una solución funcional son incidentales.

2) Código de hinchazón

Estimo que la cantidad de código producido en un ADM es de 5 a 10 veces mayor que el que crearía una solución OOP / RDM. Esto se explica porque quizás el 50% es la repetición del código, el 30% es el código de la placa de caldera, y el 20% es la resolución o resolución de problemas que surgen de la falta de un RDM.

3) Pobre comprensión de los problemas de dominio

Un ADM y una pobre comprensión de los problemas del dominio van de la mano. Surgen soluciones ingenuas, los requisitos son poco considerados debido a la dificultad de soportarlos con el DM existente, y el ADM se convierte en una barrera importante para la innovación empresarial dado el mayor tiempo de desarrollo y la falta de flexibilidad.

4) Dificultad de mantenimiento

Se requiere un cierto nivel de rigor para garantizar que el concepto de dominio se modifique en todos los lugares en los que se expresa, dado que el concepto puede no ser solo una repetición de copiar y pegar. Esto a menudo lleva a que se investiguen los mismos errores y se solucionen en múltiples ocasiones.

5) Mayor dificultad con el embarque

Creo que uno de los beneficios de un RDM es la cohesión de conceptos que permiten una comprensión más rápida del dominio. Con un ADM, los conceptos pueden estar fragmentados y carecer de claridad, por lo tanto, es más difícil para los nuevos desarrolladores adquirirlos.

También estuve tentado de incluir los costos de soporte operacional para un ADM más alto que para un RDM, pero esto dependería de una serie de factores.

Como han señalado otros, mire a DDD (Greg Evans, Vince Vaughn y Scott Millett) para conocer los beneficios de un RDM.


El modelo de dominio anémico (ADM) puede ser una buena opción si su equipo no puede o no quiere construir un modelo de dominio enriquecido (RDM) y mantenerlo a lo largo del tiempo. Ganar con un RDM requiere una cuidadosa atención a las abstracciones dominantes utilizadas en el sistema. Figura que, en cualquier grupo de desarrollo, no más de la mitad y tal vez solo una décima parte de sus miembros son competentes con las abstracciones. A menos que este cuadro (tal vez solo un desarrollador) sea capaz de mantener la influencia sobre las actividades de todo el grupo, el RDM sucumbirá a la entropía.

Y el RDM entrópico duele, de maneras particulares. Sus desarrolladores aprenderán duras lecciones. Al principio, podrán cumplir las expectativas de sus grupos de interés, ya que no tendrán un historial al que estar a la altura. Pero a medida que su sistema se vuelve más complicado (no complejo) se volverá frágil; los desarrolladores intentarán reutilizar el código pero tienden a inducir nuevos errores o retrocesos en el desarrollo (y por lo tanto rebasar sus estimaciones).

En contraste, los desarrolladores de ADM establecerán expectativas más bajas para ellos mismos, ya que no esperarán reutilizar tanto código para las nuevas características. Con el tiempo, tendrán un sistema con muchas inconsistencias, pero probablemente no se romperá inesperadamente. Su tiempo de comercialización será más largo que con un RDM exitoso, pero es improbable que sus partes interesadas perciban esta posibilidad.


En línea con la respuesta de Eric P y lo que escribieron algunos más arriba, parece que la principal desventaja de un ADM es la pérdida de OOD, específicamente de mantener la lógica y los datos de un concepto de dominio juntos para que los detalles de implementación estén ocultos mientras la API puede ser rica

Eric continúa señalando que a menudo hay información fuera de una clase de dominio que es necesaria para la lógica de actuar en esa clase, como verificar un inventario antes de agregar un artículo a una orden. Sin embargo, me pregunto si la respuesta es una capa de servicio que contiene esta lógica global o si se maneja mejor como parte del diseño del objeto. Alguien tiene que saber sobre el objeto Inventario, el objeto Producto y el objeto Orden. Tal vez es simplemente un objeto OrderSystem, que tiene un miembro de Inventory, una lista de pedidos, etc. Esto no se verá muy diferente de un Servicio, pero creo que es conceptualmente más coherente.

O mire de esta manera: puede tener un usuario con un saldo de crédito interno, y cada vez que se llama a User.addItemToOrder (artículo) obtiene el precio del artículo y verifica el crédito antes de agregarlo, etc. Parece un OO razonable diseño. No estoy seguro de qué se pierde al reemplazar eso con Service.addItemToUserOrder (usuario, artículo), pero tampoco estoy seguro de qué se ganó. Supongo que una pérdida sería la capa adicional de código, más el estilo de escritura más torpe y la ignorancia forzada del modelo de dominio subyacente.


Es el mismo profesional que con la mayoría de los antipatrones: te permite mantener a muchas personas ocupadas durante mucho tiempo. Como a los gerentes se les paga más cuando administran a más personas, existe un fuerte incentivo para no mejorar.


Los profesionales:

  • Puedes reclamar que es un modelo de dominio y presumir ante tus amigos desarrolladores y ponerlo en tu currículum.
  • Es fácil generar automágicamente a partir de tablas de bases de datos.
  • Se asigna a objetos de transferencia de datos sorprendentemente bien.

Los contras:

  • Su lógica de dominio existe en otro lugar, probablemente en una clase llena de métodos de clase (estáticos). O su código GUI. O en múltiples lugares, todos con lógica conflictiva.
  • Es un antipatrón, por lo que otros desarrolladores te preguntarán si entiendes los conceptos de diseño orientado a objetos.

Me parece que la principal objeción de Fowler es que los ADM no son OO, en el siguiente sentido. Si uno diseña un sistema "desde cero" alrededor de las estructuras de datos pasivas que son manipuladas por otras piezas de código, entonces esto ciertamente huele más como un diseño de procedimiento que como un diseño orientado a objetos.

Sugiero que haya al menos dos fuerzas que pueden producir este tipo de diseño:

  1. Los diseñadores / programadores que aún piensan que se les requiere procedimentalmente que trabajen en un entorno orientado a objetos (o supongan que pueden ...) producir un nuevo sistema, y

  2. Los desarrolladores trabajan para poner una "cara" similar a un servicio en un sistema heredado diseñado en una forma no OO (independientemente del idioma).

Si, por ejemplo, uno construyera un conjunto de servicios para exponer la funcionalidad de una aplicación de mainframe de COBOL existente, se podrían definir servicios e interfaces en términos de un modelo conceptual que no refleje las estructuras internas de datos de COBOL. Sin embargo, si el servicio mapea el nuevo modelo con los datos heredados para usar la implementación existente pero oculta, entonces el nuevo modelo podría ser "anémico" en el sentido del artículo de Fowler, por ejemplo, un conjunto de definiciones de estilo TransferObject y las relaciones sin un comportamiento real.

Este tipo de compromiso puede ser muy común para los límites en los que los sistemas OO idealista- mente puros deben interactuar con un entorno existente que no sea OO.


Mi equipo prefiere personalmente el ADM. tenemos un conjunto de objetos comerciales que representan partes específicas de nuestro dominio. Usamos servicios para guardar estos objetos en el db. Nuestros objetos comerciales tienen métodos, sin embargo, estos métodos solo manipulan su estado interno.

El beneficio para nosotros en el uso de ADM sobre RDM se puede ver en cómo persistimos los objetos a db. Los desarrolladores que trabajen en nuestros sistemas de códigos heredados pueden usar nuestros objetos comerciales (del nuevo sistema) y continuar usando su capa de acceso a datos actual para persistir estos objetos en el DB. El uso de RDM obligaría a los desarrolladores de nuestro sistema heredado a inyectar objetos de repositorio en nuestro modelo comercial ... lo que no sería coherente con su capa de acceso a datos actual.


Para extender la respuesta de Michael, hubiera pensado que es (bastante) claro dónde debe ir ese código: en un Mediador dedicado que maneje la interacción entre el Pedido y el Inventario.

Desde mi punto de vista, la clave del dominio es que DEBE mantener el comportamiento de prueba simple, los métodos isInThisState() , etc. En mi experiencia, estos también están dispersos a lo largo del servicio lágrimas (sic :)) en la mayoría de las empresas y se copian interminablemente. reescrito Todo lo cual rompe las reglas de conhesion estandar.

En mi opinión, el enfoque debería ser apuntar a un DM que mantenga la mayor parte posible del comportamiento de la empresa, poner el resto en áreas claramente designadas (es decir, no en los servicios).