domain driven design - que - Tener problemas para poner la lógica del mundo real en la capa de dominio DDD
que es ddd (3)
Un usuario se registra para un servicio. El usuario permanece en la base de datos, se genera y guarda un archivo (necesario para la cuenta del usuario) y se envía un correo electrónico de confirmación.
Puede aplicar el principio de inversión de dependencia aquí. Defina una interfaz de dominio como esta:
void ICanSendConfirmationEmail(EmailAddress address, ...)
o
void ICanNotifyUserOfSuccessfulRegistration(EmailAddress address, ...)
La interfaz puede ser utilizada por otras clases de dominio. Implementar esta interfaz en la capa de infraestructura, utilizando clases SMTP reales. Inyectar esta implementación en el inicio de la aplicación. De esta manera, declaró la intención comercial en el código de dominio y la lógica de su dominio no tiene referencia directa a la infraestructura SMTP. La clave aquí es el nombre de la interfaz, debe basarse en el lenguaje ubicuo.
Una canción se compra en una tienda de música digital. La cesta de la compra está vacía. La compra se mantiene. El servicio de pago se llama. Se envía una confirmación por correo electrónico. Ok, esto podría estar relacionado con el primer ejemplo. La pregunta aquí es, ¿quién es responsable de organizar esta transacción?
Utilice las mejores prácticas de OOP para asignar responsabilidades (GRASP y SOLID). Las pruebas unitarias y la refactorización le darán una respuesta de diseño. La propia orquestación puede ser parte de la capa de aplicación delgada . Desde DDD Layered Architecture :
Capa de aplicación : define los trabajos que el software debe hacer y dirige los objetos del dominio expresivo para resolver problemas. Las tareas de las que es responsable esta capa son significativas para el negocio o necesarias para la interacción con las capas de aplicación de otros sistemas.
Esta capa se mantiene delgada. No contiene conocimientos o reglas comerciales, sino que solo coordina las tareas y los delegados trabajan para colaboraciones de objetos de dominio en la siguiente capa hacia abajo. No tiene un estado que refleje la situación del negocio, pero puede tener un estado que refleje el progreso de una tarea para el usuario o el programa.
A pesar de haber estudiado Domain Driven Design
por Domain Driven Design
durante mucho tiempo, todavía hay algunos conceptos básicos que simplemente he descubierto.
Parece que cada vez que trato de diseñar una domain layer
rica, todavía necesito muchos Domain Services
de Domain Services
o una Application Layer
gruesa, y termino con un grupo de entidades de dominio casi anémicas sin una lógica real en ellas, aparte de "GetTotalAmount" y similares. El problema clave es que las entidades no son conscientes de cosas externas, y es una mala práctica inyectar algo en las entidades.
Déjame dar algunos ejemplos:
1. Un usuario se registra para un servicio. El usuario permanece en la base de datos, se genera y guarda un archivo (necesario para la cuenta del usuario) y se envía un correo electrónico de confirmación.
El ejemplo con el correo electrónico de confirmación se ha analizado en gran medida en otros subprocesos, pero sin una conclusión real. Algunos sugieren colocar la lógica en un application service
que recibe un servicio de EmailService
y application service
EmailService
inyectados desde la infrastructure layer
. Pero entonces tendría lógica de negocios fuera del dominio, ¿verdad? Otros sugieren crear un domain service
que inyecte los infrastructure services
, pero en ese caso necesitaría tener las interfaces de los infrastructure services
dentro de la domain layer
( IEmailService
e IFileService
), que tampoco se ve muy bien (porque la domain layer
no se puede hacer referencia a la infrastructure layer
). Y otros sugieren implementar los Eventos de Dominio de Udi Dahan y luego hacer que EmailService y FileService se suscriban a esos eventos. Pero eso parece una implementación muy suelta, ¿y qué sucede si fallan los servicios? Por favor, déjame saber cuál crees que es la solución correcta aquí.
2. Una canción es comprada en una tienda de música digital. La cesta de la compra está vacía. La compra se mantiene. El servicio de pago se llama. Se envía una confirmación por correo electrónico.
Ok, esto podría estar relacionado con el primer ejemplo. La pregunta aquí es, ¿quién es responsable de organizar esta transacción? Por supuesto que podría poner todo en el controlador MVC con servicios inyectados. Pero si quiero DDD real, toda la lógica empresarial debería estar en el dominio. ¿Pero qué entidad debería tener el método de "Compra"? Song.Purchase()
? Order.Purchase()
¿ Order.Purchase()
? OrderProcessor.Purchase()
(servicio de dominio)? ShoppingCartService.Purchase()
(servicio de aplicación?)
Este es un caso en el que creo que es muy difícil utilizar una lógica empresarial real dentro de las entidades de dominio. Si no es una buena práctica inyectar algo en las entidades, ¿cómo pueden hacer algo más que verificar su propio estado (y el de su agregado)?
Espero que estos ejemplos sean lo suficientemente claros para mostrar los problemas con los que estoy tratando.
Gran parte de sus solicitudes están relacionadas con el diseño orientado a objetos y la asignación de responsabilidades, puede pensar en los patrones GRASP y This , puede beneficiarse de los libros de diseño orientados a objetos, recomiende lo siguiente
La respuesta de Dimitry señala algunas cosas buenas para buscar. A menudo / fácilmente te encuentras en tu escenario, con un intercambio de datos desde db hasta GUI a través de diferentes capas.
Me inspiré en los simples consejos de Jimmy Nilsson "Objetos de valor, objetos de valor y más objetos de valor". A menudo las personas tienden a centrarse mucho en los sustantivos y los modelan como entidades. Naturalmente, a menudo tiene problemas para encontrar un comportamiento DDD. Los verbos son más fáciles de asociar con el comportamiento. Una buena cosa es hacer que estos verbos aparezcan en su dominio como objetos de valor.
Alguna guía que utilizo para mí cuando intento desarrollar el dominio (debo decir que se necesita tiempo para construir un dominio rico, a menudo varias iteraciones de refactorización ...):
- Minimizar propiedades (obtener / establecer)
- Usa objetos de valor tanto como puedas
- Exponga lo menos que pueda. Haz que tu dominio agregue métodos intuitivos.
No olvides que tu Dominio puede ser rico haciendo Validación. Solo su dominio sabe cómo realizar una compra y lo que se requiere.
Su dominio también debe ser responsable de la validación cuando sus entidades hacen una transición de un estado a otro estado (validaciones de flujo de trabajo).
Le daré algunos ejemplos: Aquí hay un artículo que escribí en mi blog sobre su problema sobre el dominio anémico http://magnusbackeus.wordpress.com/2011/05/31/preventing-anemic-domain-model-where-is-my-model-behaviour/
También puedo recomendar el artículo del blog de Jimmy Bogard sobre las validaciones de entidades y el uso del patrón Validator junto con los métodos de extensión. Le brinda la libertad de validar elementos de infraestructura sin ensuciar su dominio: http://lostechies.com/jimmybogard/2007/10/24/entity-validation-with-visitors-and-extension-methods/
Uso los eventos de dominio de Udi con gran éxito. También puede hacer que sean asíncronos si cree que su servicio puede fallar. También lo envuelve en una transacción (usando el framework NServiceBus).
En su primer ejemplo (solo haga una lluvia de ideas ahora para que nuestras mentes piensen más en objetos de valor).
- Su servicio de aplicación
MusicService.AddSubscriber(User newUser)
recibe una llamada de un presentador / controlador / WCF con un nuevo usuario. El servicio ya tieneIUserRepository
eIMusicServiceRepository
inyectados en ctor. - El servicio de música "Spotify" se carga a través de IMusicServiceRepository
- El
musicService.SignUp(MusicServiceSubscriber newSubsriber)
entidadmusicService.SignUp(MusicServiceSubscriber newSubsriber)
toma un objeto ValueMusicServiceSubscriber
. Este objeto de valor debe tener usuario y otros objetos obligatorios en ctor (los objetos de valor son inmutables). Aquí también puede colocar lógica / comportamiento como manejar ID de suscripción, etc. - Lo que también hace el método SignUp, dispara un evento de dominio
NewSubscriberAddedToMusicService
. Puede ser atrapado porEventHandler HandleNewSubscriberAddedToMusicServiceEvent
que tieneIFileService
eIEmailService
inyectados en su ctor. La implementación de este controlador está ubicada en la capa de Servicio de Aplicación, PERO el evento está controlado por Dominio yMusicService.SignUp
. Esto significa que el dominio está en control. Eventhandler crea un archivo y envía un correo electrónico.
Puede persistir al usuario a través del administrador de eventos O hacer que el método MusicService.AddSubscriber(...)
este. Ambos lo harán a través de IUserRepository
pero es una cuestión de gusto y tal vez cómo reflejará el dominio real.
Finalmente ... espero que captes algo de lo anterior ... de todos modos. Lo más importante es comenzar a agregar métodos "Verbos" a las entidades y hacer la colaboración. También puede tener objetos en su dominio que no son persistentes, solo están allí para mediar entre varias entidades de dominio y pueden alojar algoritmos, etc.