Con todos estos servicios, ¿cómo no puedo estar anémico?


90

¿Dónde trazamos la línea entre la delegación y la encapsulación de la lógica empresarial? Me parece que cuanto más delegamos, más anémicos nos volvemos. Sin embargo, la delegación también promueve la reutilización y el director DRY. Entonces, ¿qué es apropiado delegar y qué debe permanecer en nuestros modelos de dominio?

Tome las siguientes preocupaciones como ejemplos:

Autorización . ¿Debería el objeto de dominio ser responsable de mantener sus reglas de control de acceso (como una propiedad CanEdit) o ​​debería delegarse en otro componente / servicio exclusivamente responsable de administrar el acceso, por ejemplo, IAuthorizationService.CanEdit (objeto)? ¿O debería ser una combinación de los dos? ¿Quizás el objeto de dominio tiene una propiedad CanEdit que delega a un IAuthorizationService interno para realizar el trabajo real?

Validación . La misma discusión que la anterior se relaciona con la validación. ¿Quién mantiene las reglas y quién es responsable de evaluarlas? Por un lado, el estado del objeto debe pertenecer a ese objeto y la validez es un estado, pero no queremos reescribir el código utilizado para evaluar las reglas para cada objeto de dominio. Nosotros podríamos utilizar la herencia en este caso ...

Creación de objetos . Clase de fábrica versus métodos de fábrica versus 'novedad' en una instancia. Si utilizamos una clase de fábrica separada, podemos aislar y encapsular la lógica de creación, pero a expensas de abrir el estado de nuestro objeto a la fábrica. Esto se puede administrar si nuestra capa de dominio está en un ensamblaje separado al exponer un constructor interno utilizado por la fábrica, pero esto se convierte en un problema si hay múltiples patrones de creación. Y, si todo lo que hace la fábrica es llamar al constructor correcto, ¿cuál es el punto de tener la fábrica?

Los métodos de fábrica en la clase eliminan el problema de abrir el estado interno del objeto, pero como son estáticos, no podemos romper las dependencias mediante la inyección de una interfaz de fábrica como podemos hacerlo con una clase de fábrica separada.

Persistencia . Se podría argumentar que si nuestro objeto de dominio va a exponer CanEdit mientras se delega la responsabilidad de realizar la verificación de autorización a otra parte (IAuthorizationService), ¿por qué no tener un método Save en nuestro objeto de dominio que haga lo mismo? Esto nos permitiría evaluar el estado interno del objeto para determinar si la operación se puede realizar sin romper la encapsulación. Por supuesto, requiere que inyectemos la instancia del repositorio en nuestro objeto de dominio, lo que me huele un poco, así que, ¿generamos un evento de dominio y permitimos que un controlador realice la operación de persistencia?

¿Ves a dónde voy con esto?

Rockford Lhotka tiene una gran discusión sobre sus razones para seguir la ruta de la Clase a Cargo para su marco CSLA y tengo un poco de historia con ese marco y puedo ver su idea de los objetos de negocios paralelos a los objetos de dominio de muchas maneras. Pero tratando de ser más adherente a los buenos ideales DDD, me pregunto cuándo la colaboración se vuelve demasiado.

Si termino con un IAuthorizationService, IValidator, IFactory e IRepository para mi raíz agregada, ¿qué queda? ¿Es suficiente tener un método de publicación que cambie el estado del objeto de borrador a publicado para considerar que la clase es un objeto de dominio no anémico?

¿Tus pensamientos?


Gran pregunta No tengo una respuesta para usted, ya que casi siempre termino completamente anémica en diseño por la misma razón: usando / consumiendo / exponiendo servicios desde / a muchos contextos diferentes o diferentes aplicaciones.
hromanko

Gran pregunta, me encantaría ver a Unclebob, Martinfowler, Ericevans y otros para que intervengan en esto. Ahora para que me vaya y tenga una larga reflexión
Martijn Verburg

Me encuentro evolucionando a un modelo anémico todo el tiempo; y entonces estoy luchando con exactamente lo mismo. Gran pregunta!
L-Four

Esta ha sido mi experiencia con DDD también. Lo estamos haciendo donde trabajo, y siempre terminamos anémicos. Anteriormente (y todavía lo hago) uso Csla. A nuestro arquitecto no le gusta que estemos anémicos, pero no ha podido darme una buena respuesta a lo que debe hacer el objeto si no se pueden hacer todas las cosas que usted señala dentro del objeto. Al final del día, tratar de ser purista DDD parece crear más trabajo de lo que vale. Personalmente, creo que Csla y DDD se llevan bien (parecen idénticos en los principios), si estás dispuesto a dejar atrás el dogma DDD.
Andy

Aquí hay un ejemplo de algunas técnicas utilizadas al modelar dominios desde una perspectiva de comportamiento (no centrada en datos): medium.com/@wrong.about/…
Zapadlo

Respuestas:


66

La mayor parte de la confusión parece estar relacionada con la funcionalidad que no debería existir en absoluto en el modelo de dominio:

  • La persistencia nunca debe estar en el modelo de dominio. Nunca jamás. Esa es la razón por la que confía en los tipos abstractos, como IRepositorysi parte del modelo alguna vez necesita hacer algo como recuperar una parte diferente del modelo y usar inyección de dependencia o alguna técnica similar para conectar la implementación. Así que elimínalo del registro.

  • La autorización generalmente no es parte de su modelo de dominio, a menos que sea parte del dominio, por ejemplo, si está escribiendo software de seguridad. La mecánica de quién puede realizar qué en una aplicación normalmente se maneja en el "borde" del nivel de negocio / dominio, las partes públicas con las que las partes de la interfaz de usuario y la integración realmente pueden hablar: el controlador en MVC, los servicios o el sistema de mensajería en sí mismo en una SOA ... obtienes la imagen.

  • Las fábricas (y supongo que se refiere a fábricas abstractas aquí) no son exactamente malas para tener en un modelo de dominio, pero casi siempre son innecesarias. Normalmente solo tienes una fábrica cuando la mecánica interna de la creación de objetos puede cambiar. Pero solo tiene una implementación del modelo de dominio, lo que significa que solo habrá un tipo de fábrica que siempre invoque los mismos constructores y otro código de inicialización.

    Puede tener fábricas de "conveniencia" si lo desea, clases que encapsulan combinaciones comunes de parámetros de constructor, etc., pero honestamente, en términos generales, si tiene muchas fábricas en su modelo de dominio, entonces solo está desperdiciando líneas de código

Entonces, una vez que elimines todo eso, eso solo deja la validación. Ese es el único que es un poco complicado.

La validación es parte de su modelo de dominio, pero también es parte de todos los demás componentes de la aplicación. Su interfaz de usuario y su base de datos tendrán sus propias reglas de validación similares pero diferentes, basadas en un modelo conceptual similar pero diferente. No se especifica realmente si los objetos necesitan o no un Validatemétodo, pero incluso si lo tienen, generalmente lo delegarán a una clase de validador (no a la interfaz; la validación no es abstracta en el modelo de dominio, es fundamental).

Tenga en cuenta que el validador sigue siendo técnicamente parte del modelo; no necesita estar conectado a una raíz agregada porque no contiene ningún dato o estado. Los modelos de dominio son elementos conceptuales, que generalmente se traducen físicamente en un conjunto o una colección de conjuntos. No se preocupe por el problema "anémico" si su código de delegación reside muy cerca del modelo de objetos; Todavía cuenta.

Lo que todo esto realmente se reduce a es que si vas a hacer DDD, usted tiene que entender lo que el dominio es . Si todavía estás hablando de cosas como la persistencia y la autorización, entonces estás en el camino equivocado. El dominio representa el estado de ejecución de un sistema: los objetos y atributos físicos y conceptuales. Cualquier cosa que no sea directamente relevante para los objetos y las relaciones en sí no pertenece al modelo de dominio, punto.

Como regla general, al considerar si algo pertenece o no en el modelo de dominio, hágase la siguiente pregunta:

"¿Puede esta funcionalidad cambiar alguna vez por razones puramente técnicas?" En otras palabras, ¿no se debe a algún cambio observable en el negocio o dominio del mundo real?

Si la respuesta es "sí", entonces no pertenece al modelo de dominio. No es parte del dominio.

Existe una muy buena posibilidad de que, algún día, cambie sus infraestructuras de persistencia y autorización. Por lo tanto, no son parte del dominio, son parte de la aplicación. Esto también se aplica a algoritmos, como ordenar y buscar; no debe ir e introducir una implementación de código de búsqueda binaria en su modelo de dominio, porque su dominio solo se ocupa del concepto abstracto de búsqueda, no de cómo funciona.

Si, después de haber eliminado todas las cosas que no importan, encuentra que el modelo de dominio es realmente anémico , entonces eso debería servir como una buena indicación de que DDD es simplemente el paradigma incorrecto para su proyecto.

Algunos dominios son realmente anémicos. Las aplicaciones de marcadores sociales realmente no tienen mucho de un "dominio" para hablar; Todos sus objetos son básicamente datos sin funcionalidad. Un sistema de ventas y CRM, por otro lado, tiene un dominio bastante pesado; cuando carga una Rateentidad, existe una expectativa razonable de que realmente puede hacer cosas con esa tasa, como aplicarla a una cantidad de pedido y hacer que descubra los descuentos por volumen y los códigos de promoción y todas esas cosas divertidas.

Objetos de dominio que acaba de celebrar los datos por lo general no significa que usted tiene un modelo de dominio anémico, pero eso no necesariamente significa que se ha creado un mal diseño - que sólo podría significar que el dominio de sí mismo es anémica y que usted debe utilizar una metodología diferente


2
Además, @SonOfPirate, no es del todo improbable que desee cambiar su modelo de seguridad completo en algún momento; la seguridad basada en roles a menudo queda obsoleta a favor de la seguridad basada en reclamos o derechos o tal vez incluso desee seguridad a nivel de campo. Imagínese tratando de reelaborar todo su modelo de dominio si esto sucediera.
Aaronaught

1
@SonOfPirate: Casi parece que todavía estás un poco atascado en el viejo modelo de "nivel de negocios" en el que había un "nivel medio" que era básicamente una capa delgada sobre la capa de datos, implementando varias reglas de negocios y también reglas de seguridad . Eso no es lo que es la capa de dominio. El dominio es de lo que todo lo demás depende, representa los objetos y las relaciones del mundo real que el sistema debe administrar.
Aaronaught

1
@ LaurentBourgault-Roy: Lo siento, no te creo. Cada compañía podría decir eso sobre cada aplicación; después de todo, cambiar una base de datos es difícil. Eso no lo hace parte de su dominio, y la lógica de negocios que está acoplada a la capa de persistencia solo significa mala abstracción. Un modelo de dominio se centra en el comportamiento, que es exactamente lo que no es la persistencia . Este no es un tema sobre el cual las personas puedan inventar sus propias definiciones; está claramente explicado en DDD. A menudo no necesita un modelo de dominio para CRUD o aplicaciones de informes, pero tampoco debe afirmar que tiene uno cuando no lo tiene.
Aaronaught

1
La autorización pertenece absolutamente a la capa de dominio. ¿Quién decide qué permisos existen? El negocio lo hace. ¿Quién decide quién puede hacer qué? El negocio lo hace. Hace unas semanas recibimos una solicitud de función para cambiar la autorización necesaria para editar un objeto en particular en nuestro sistema. Si una plantilla se basaba en una plantilla maestra, entonces se necesitaban mayores privilegios para editar (anular los valores del maestro) de la plantilla que normalmente necesitaría. ¿Dónde pertenece esa lógica si no está en el dominio?
Andy

1
Otro tipo de autorización podría ser un límite de cuenta de clientes. La gente normal de servicio al cliente puede elevarlo a un cierto punto, pero tal vez los límites más altos necesitan aprobación de la gerencia. Esa es la lógica de autorización.
Andy

6

Autorización. Si el objeto de dominio es responsable de mantener sus reglas de control de acceso

No. La autorización es una preocupación en sí misma. Los comandos que no serían válidos debido a la falta de permisos deben rechazarse antes del dominio, lo antes posible, lo que significa que a menudo incluso querremos verificar la autorización de un comando potencial para construir la interfaz de usuario (para no incluso mostrarle al usuario la opción de editar).

Compartir estrategias de autorización entre capas (en la interfaz de usuario y más arriba en un controlador de servicio o comando) es más fácil cuando la autorización se compone por separado del modelo de dominio.

Una parte difícil que se puede encontrar es la autorización contextual, donde un comando puede o no permitirse no solo en función de los roles de los usuarios sino también de los datos / reglas de la empresa.

Validación. La misma discusión que la anterior se relaciona con la validación.

También diría que no, no en el dominio (principalmente). La validación ocurre en diferentes contextos, y las reglas de validación a menudo difieren entre contextos. Rara vez existe un sentido simple y absoluto de válido o no válido cuando se consideran los datos encapsulados por un agregado.

Además, al igual que la autorización, utilizamos la lógica de validación en todas las capas: en la interfaz de usuario, en el servicio o en el controlador de comandos, etc. Una vez más, es más fácil emplear DRY con validación si es un componente separado. Desde un punto de vista práctico, la validación (particularmente cuando se usan frameworks) requiere exponer datos que de otro modo deberían encapsularse y a menudo requiere que se adjunten atributos personalizados a campos y propiedades. Prefiero que estos estén en otras clases que mis modelos de dominio.

Prefiero duplicar algunas propiedades en un par de clases similares que intentar forzar los requisitos para un marco de validación en mis entidades. Eso inevitablemente termina haciendo un desastre de las clases de entidad.

Creación de objetos. Clase de fábrica versus métodos de fábrica versus 'novedad' en una instancia.

Yo uso una capa de indirección. En mis proyectos más recientes, este es un comando + controlador para crear algo, es decir CreateNewAccountCommand. Una alternativa podría ser usar siempre una fábrica (aunque eso puede ser incómodo si el resto de las operaciones de una entidad están expuestas por una clase de servicio que está separada de la clase de fábrica).

En general, sin embargo, trato de ser más flexible con las opciones de diseño para la creación de objetos. newEs fácil y familiar, pero no siempre es suficiente. Creo que usar el juicio aquí y permitir que diferentes partes de un sistema usen diferentes estrategias según sea necesario es importante.

Persistencia. ... por qué no tener un método Save en nuestro objeto de dominio

Rara vez es una buena idea; Creo que hay mucha experiencia compartida para respaldar eso.

Si termino con un IAuthorizationService, IValidator, IFactory e IRepository para mi raíz agregada, ¿qué queda? ¿Tener un método de publicación que cambie el estado del objeto de Borrador a Publicado lo suficiente como para considerar a la clase un objeto de dominio no anémico?

Quizás un modelo de dominio no sea la opción correcta para esta parte de su aplicación.


2
"Una parte difícil que se puede encontrar es la autorización contextual, donde un comando puede o no permitirse no solo en función de los roles de los usuarios sino también de los datos / reglas de la empresa". - ¿Y cómo abordas esto? Más de las veces, al menos para mí, nuestras reglas de autorización son una combinación de roles y estado actual.
SonOfPirate

@SonOfPirate: controladores de eventos que escuchan eventos de dominio y actualizan tablas que son muy específicas para las necesidades de las consultas que verifican la autorización. Lógica que analiza el estado y determina si un rol o individuo está autorizado o no vive en el controlador de eventos (por lo que las tablas son casi siempre un simple sí / no, lo que hace que la verificación de autenticación sea una simple búsqueda). Además, tan pronto como se use algo de esa lógica en más de un lugar, se reestructurará del controlador y se convertirá en un servicio compartido.
quentin-starin

1
En general, en los últimos años, me he alejado cada vez más de tratar de consolidar todo en un dominio u objeto comercial. Mi experiencia parece ser que hacer las cosas más granulares y menos acopladas es una victoria a largo plazo. Entonces, si bien desde un punto de vista este método coloca la lógica de negocios fuera del dominio (hasta cierto punto), también admite modificaciones ágiles más adelante. Se trata de lograr un equilibrio.
quentin-starin

44
Haciendo referencia a la respuesta en sí aquí: he encontrado que el patrón de estado es muy útil cuando se trata de validaciones que dependen tanto de los permisos como de las reglas comerciales. Cree un estado abstracto que se inyecte en el modelo de dominio y exponga métodos que toman el objeto de dominio como parámetro y validan acciones específicas del dominio. De esta manera, si sus reglas de permiso cambian (como casi siempre lo hacen), nunca tendrá que meterse con el modelo para acomodarlo, porque las implementaciones de estado viven en su componente de seguridad.
Aaronaught

1
Manteniéndome en el tema de la autorización, déjame arrojar un ejemplo tangible sobre la mesa para ver cómo ustedes (ambos) lo manejarían. Tengo una operación de publicación en mi objeto de dominio que requiere que el usuario esté en el rol de editor Y que el objeto esté en un cierto estado. Quiero ocultar o deshabilitar el "botón" Publicar en la interfaz de usuario. ¿Cómo lograrías esto?
SonOfPirate

4

Vale, aquí va por mí. Voy a evitar esto diciendo que:

  • La optimización prematura (y eso incluye el diseño) a menudo puede causar problemas.

  • IANMF (no soy Martin Fowler);)

  • Un pequeño secreto sucio es que en proyectos de pequeño tamaño (incluso posiblemente medianos), lo que importa es la consistencia de su enfoque.

Autorización

Para mí, la autenticación y la autorización siempre es una preocupación transversal. En mi pequeño y feliz mundo Java, eso se delega a Spring Security o al marco Apache Shiro.

Validación Para mí, la validación es parte del objeto, ya que lo veo como la definición del objeto.

Por ejemplo, un objeto Car tiene 4 ruedas (OK, hay algunas excepciones raras, pero ignoremos el extraño Car de 3 ruedas por ahora). Un automóvil simplemente no es válido a menos que tenga 4 (en mi mundo), por lo que la validación es parte de la definición de un automóvil. Eso no significa que no pueda tener una clase de validación auxiliar.

En mi feliz mundo Java uso marcos de validación de Bean y uso anotaciones simples en la mayoría de mis campos de Bean. Es fácil validar su objeto sin importar en qué capa se encuentre.

Creación de objetos

Veo las clases de Fábrica con precaución. Demasiado a menudo he visto la xyxFactoryFactoryclase;)

Tiendo a crear un newobjeto según sea necesario, hasta que me encuentro con un caso en el que se justifica la inyección de dependencia (y dado que trato de seguir un enfoque TDD, esto aparece con mayor frecuencia).

En mi feliz mundo Java, eso es cada vez más Guice, pero de Spring sigue siendo el Rey aquí.

Persistencia

Así que este es un debate que se desarrolla en círculos y rotondas y siempre tengo dos ideas al respecto.

Algunos dicen que si miras el objeto de una manera "pura", la persistencia no es una propiedad central, es simplemente una preocupación externa.

Otros consideran que sus objetos de dominio implementan implícitamente una interfaz 'persistente' (sí, sé que me estoy estirando aquí). Por lo tanto, está bien tener varios métodos save, deleteetc. en ellos. Esto se ve como un enfoque pragmático y muchas tecnologías ORM (JPA en mi feliz mundo Java) tratan los objetos de esta manera.

Por una cuestión de seguridad transversal, me aseguro de que los permisos de edición / eliminación / agregar / lo que se establezcan correctamente en el servicio que llama al método guardar / actualizar / eliminar en el objeto. Si soy realmente paranoico, incluso podría establecer los permisos en el objeto de dominio en sí.

HTH!


2

Jimmy Nilsson toca este tema en su libro sobre DDD. Comenzó con un modelo anémico, pasó a modelos no anémicos en un proyecto posterior y finalmente se decidió por modelos anémicos. Su razonamiento fue que los modelos anémicos podrían reutilizarse en múltiples servicios con diferentes lógicas comerciales.

La compensación es la falta de capacidad de descubrimiento. Los métodos que puede utilizar para operar en sus modelos anémicos se extienden a través de un conjunto de servicios ubicados en otros lugares.


Suena como un requisito específico: la reutilización de la estructura de los datos ('datos' de estrés) lleva a que esa parte común se reduzca a DTO simples.
Victor Sergienko

¿Los modelos anémicos permiten una mejor reutilización? Suena más como un DTO, y personalmente no me importa en absoluto reutilizar las definiciones de propiedad. Prefiero reutilizar los comportamientos.
Andy

@Andy - Sin embargo, estaría de acuerdo si sus comportamientos están dentro de los Servicios de dominio y operan con objetos anémicos (está bien DTO si lo desea), ¿eso no aumenta la reutilización de esos comportamientos? Solo jugando al abogado del diablo.
jpierson

@jpierson Sin embargo, he descubierto que los comportamientos suelen ser específicos de un caso de uso en particular. Si hay reutilización, eso pertenecería a otra clase, pero el consumidor no usaría esas clases, usaría las específicas para el caso de uso. Entonces, cualquier reutilización es "detrás de escena", por así decirlo. Además, tratar de reutilizar modelos generalmente hace que los modelos sean más difíciles de usar para el consumidor, por lo que termina creando modelos de visualización / edición en la capa de la interfaz de usuario. A menudo, terminas violando DRY para brindar una experiencia de usuario más rica (por ejemplo, anotaciones de datos de modelos de edición).
Andy

1
Prefiero los modelos de dominio creados para el caso de uso, reutilizados donde tenga sentido (es decir, la reutilización se puede hacer sin modificar mucho o nada el comportamiento). Entonces, en lugar de un modelo de dominio anémico, clase de servicio y modelo de edición, tiene un único modelo de dominio editable inteligente. Mucho más fácil de usar y mantener, he encontrado.
Andy

2

Esta pregunta se hizo hace mucho tiempo, pero está etiquetada con Diseño controlado por dominio. Creo que la pregunta en sí contiene un malentendido fundamental de toda la práctica y las respuestas, incluida la respuesta aceptada, perpetúan un malentendido fundamental.

No existe "el modelo de dominio" en una arquitectura DDD.

Tomemos la autorización como ejemplo. Déjame pedirte que pienses en una pregunta: imagina que dos usuarios diferentes se autentican con tu sistema. Un usuario tiene permiso para cambiar una determinada entidad, pero el otro no. Por qué no?

Odio los ejemplos simples y artificiales porque a menudo confunden más de lo que iluminan. Pero supongamos que tenemos dos dominios diferentes. Primero es una plataforma CMS para una agencia de marketing. Esta agencia tiene muchos clientes que tienen contenido en línea que debe ser administrado por redactores y artistas gráficos. El contenido incluye publicaciones de blog, así como páginas de destino para diferentes clientes.

El otro dominio es la gestión de inventario para una empresa de calzado. El sistema gestiona el inventario desde el momento en que llega desde el fabricante en Francia, a los centros de distribución en los Estados Unidos continentales, a las tiendas minoristas en los mercados locales y, finalmente, al cliente que compra los zapatos en el comercio minorista.

Si cree que las reglas de autorización son las mismas para ambas compañías, entonces sí, sería un buen candidato para un servicio fuera del dominio. Pero dudo que las reglas de autorización sean las mismas. Incluso los conceptos detrás de los usuarios serían diferentes. Ciertamente, el lenguaje sería diferente. La agencia de marketing probablemente tiene funciones como autor de publicaciones y propietario de activos, mientras que la compañía de zapatos probablemente tiene funciones como empleado de envío o gerente de almacén o gerente de tienda.

Estos conceptos probablemente tienen todo tipo de reglas de permisos asociadas con ellos que deben modelarse en el dominio. Pero eso no significa que todos sean parte del mismo modelo, incluso dentro de la misma aplicación. Porque recuerda que hay diferentes contextos limitados.

Entonces, tal vez se podría considerar que un modelo de dominio no anémico en el contexto de Autorización es diferente del contexto de enrutamiento de envíos de calzado a tiendas con poco inventario o enrutamiento de visitantes del sitio a la página de destino apropiada, según el anuncio en el que hicieron clic.

Si se encuentra con modelos de dominio anémico, tal vez simplemente necesite pasar más tiempo en el mapeo de contexto antes de comenzar a escribir código.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.