2011-09-05 19 views
17

A pesar de haber estudiado Domain Driven Design durante mucho tiempo, todavía hay algunos conceptos básicos que simplemente entiendo.Tener problemas para poner la lógica del mundo real en la capa de dominio DDD

Parece que cada vez que intento de diseñar una rica domain layer, todavía necesita una gran cantidad de Domain Services o una gruesa Application Layer, y termino con un grupo de entidades del dominio casi anémicos sin una lógica real en ellos, aparte de "GetTotalAmount" y similares. La cuestión clave es que las entidades no son conscientes de las cosas externas, y es una mala práctica inyectar algo en las entidades.

te voy a dar algunos ejemplos:

1. Un usuario se registra para un servicio. El usuario persiste en la base de datos, se genera y guarda un archivo (necesario para la cuenta de usuario) y se envía un correo electrónico de confirmación.

El ejemplo con el correo electrónico de confirmación se ha discutido ampliamente en otros hilos, pero sin una conclusión real. Algunos sugieren poner la lógica en un application service que obtiene un EmailService y FileService inyectado desde el infrastructure layer. Pero luego tendría lógica de negocios fuera del dominio, ¿verdad? Otros sugieren la creación de un domain service que tiene la infrastructure services inyecta - pero en ese caso, tendría que tener las interfaces del infrastructure services dentro del domain layer (IEmailService y IFileService), que no se ve muy bien ya sea (porque el domain layer no se puede hacer referencia a la infrastructure layer) . Y otros sugieren implementar Udi Dahan's Domain Events y luego tener el servicio de correo electrónico y FileService suscribirse a esos eventos. Pero eso parece una implementación muy flexible, ¿y qué pasa si los servicios fallan? Por favor, hágame saber cuál cree que es la solución correcta aquí.

2. Una canción se compra en una tienda de música digital. El carrito de compras está vacío. La compra es persistente. 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 el responsable de orquestar esta transacción? Por supuesto, podría poner todo en el controlador MVC con servicios inyectados. Pero si quiero una DDD real, toda la lógica de negocios debería estar en el dominio. Pero, ¿qué entidad debe tener el método de "Compra"? Song.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 la lógica comercial real dentro de las entidades de dominio. Si no es una buena práctica inyectar algo en las entidades, ¿cómo pueden hacer otra cosa que no sea verificar su propio estado (y el de su agregado)?

Espero que estos ejemplos sean lo suficientemente claros como para mostrar los problemas que estoy tratando.

+0

DDD sugiere hacer que las entidades 'File' y' Email' formen parte del dominio.La infraestructura es responsable de generar un archivo y enviar un correo electrónico cuando las entidades correspondientes aparecen en la capa de dominio. – Lightman

Respuesta

8

Un usuario se registra para un servicio.El usuario persiste en la base de datos , se genera y guarda un archivo (necesario para la cuenta de usuario), y se envía un correo electrónico de confirmación.

Puede aplicar Dependency Inversion Principle aquí. Definir una interfaz de dominio como esto:

void ICanSendConfirmationEmail(EmailAddress address, ...) 

o

void ICanNotifyUserOfSuccessfulRegistration(EmailAddress address, ...) 

interfaz puede ser utilizada por otras clases de dominio. Implemente esta interfaz en la capa de infraestructura, utilizando clases SMTP reales. Inyecte esta implementación en el inicio de la aplicación. De esta forma, usted indicó la intención comercial en el código de dominio y su lógica de 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. El carrito de compras está vacío. La compra es persistente. 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 el responsable de orquestar esta transacción?

Utilice las mejores prácticas OOP para asignar responsabilidades (GRASP y SOLID). Las pruebas unitarias y la refactorización le darán un feedback de diseño. La orquestación en sí puede ser parte de thin Application Layer. De DDD Layered Architecture:

capa de aplicación: Define los puestos de trabajo del software se supone que debe hacer y dirige los objetos de dominio expresivos de resolver los problemas. Las tareas que esta capa es responsable son significativas para la empresa o son necesarias para la interacción con las capas de aplicación de otros sistemas .

Esta capa se mantiene delgada. No contiene reglas de negocio ni conocimiento de , pero solo coordina tareas y delega el trabajo a colaboraciones de objetos de dominio en la siguiente capa. No tiene estado que refleje la situación comercial, pero puede tener el estado que refleja el progreso de una tarea para el usuario o el programa.

+0

¡Gracias! Gran idea con las interfaces de dominio. Y después de haber comprobado más muestras de DDD, estoy convencido de que no debería temer poner * alguna * lógica de negocios en la capa de aplicación, como administrar en qué orden llamar los objetos de dominio. – lasseschou

+0

Hola, Dmitry, tengo una pregunta sobre las interfaces de dominio. ¿Recomendaría inyectar una implementación de las interfaces de dominio directamente en las entidades de dominio? (¿es esa una buena práctica?) ¿O crearía un servicio de dominio 'UserService' con una implementación inyectada y luego llamaría a ese servicio desde la capa de aplicación (' UserService.SendConfirmationEmail() ')? Todavía no es del todo claro para mí cómo implementar esta solución. – lasseschou

0

Gran parte de ustedes, las solicitudes están relacionadas con el objeto de diseño orientado y asignación de responsabilidades, se puede pensar en GRASP Patterns y This, puede beneficiarse de la orientación a objetos libros de diseño, recomiendo el siguiente

Applying UML and Patterns

11

La respuesta de Dimitry señala algunas cosas buenas que debe buscar. A menudo/fácilmente te encuentras en tu escenario, con datos que van desde db hasta GUI a través de diferentes capas.

Me he inspirado en los consejos simples de Jimmy Nilsson "Objetos de valor, objetos de valor y más objetos de valor". Con frecuencia las personas tienden a enfocarse mucho en los sustantivos y los modelan como entidades. Naturalmente, a menudo tienes problemas para encontrar el 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 orientación que uso para mí cuando trato de desarrollar el dominio (debo decir que lleva tiempo construir un dominio enriquecido, a menudo varias iteraciones de refactorización ...):

  • Minimizar propiedades (get/set)
  • El valor de uso se opone tanto como usted puede
  • exponer tan poco que pueda. Hacer que los métodos de agregación de dominio sean intuitivos.

No olvide que su dominio puede ser rico haciendo la validación. Es solo su dominio el que sabe cómo realizar una compra y qué se requiere.

Su dominio también debe ser responsable de la validación cuando sus entidades realizan una transición de un estado a otro estado (validaciones de flujo de trabajo).

te voy a dar algunos ejemplos: Aquí está un artículo que escribí en mi blog con respecto a su tema sobre el dominio anémica http://magnusbackeus.wordpress.com/2011/05/31/preventing-anemic-domain-model-where-is-my-model-behaviour/

también puedo realmente recomiendo artículo en el blog de Jimmy Bogard sobre validaciones entidad y con el patrón Validador juntos con métodos de extensión. Le da la libertad de validar las infraestructuras sin ensuciar su dominio: http://lostechies.com/jimmybogard/2007/10/24/entity-validation-with-visitors-and-extension-methods/

Utilizo los eventos de dominio de Udi con gran éxito. También puede hacer que sean asincrónicos si cree que su servicio puede fallar. También lo envuelve en una transacción (utilizando el marco NServiceBus).

En su primer ejemplo (solo haciendo una lluvia de ideas para que nuestras mentes piensen más en objetos de valor).

  1. Su servicio de aplicación MusicService.AddSubscriber(User newUser) recibe una llamada de un presentador/controlador/WCF con un nuevo usuario. El servicio ya recibió IUserRepository y IMusicServiceRepository inyectado en el ctor.
  2. El servicio de música "Spotify" se carga a través IMusicServiceRepository
  3. método entidad musicService.SignUp(MusicServiceSubscriber newSubsriber) toma un objeto de valor MusicServiceSubscriber. Este objeto de valor debe tomar el usuario y otros objetos obligatorios en ctor (los objetos de valor son inmutables). Aquí también puede colocar la lógica/comportamiento como manejar el subscriptionId, etc.
  4. Lo que el método de registro también lo hace, activa un evento de dominio NewSubscriberAddedToMusicService. Es atrapado por EventHandler HandleNewSubscriberAddedToMusicServiceEvent que tiene IFileService y IEmailService inyectado en su ctor. La implementación de este controlador se encuentra en la capa del servicio de aplicación PERO el evento está controlado por Domain y MusicService.SignUp. Esto significa que el dominio está bajo control. Eventhandler crea un archivo y envía un correo electrónico.

Puede persistir en el usuario a través de eventhandler O hacer el método MusicService.AddSubscriber(...) para esto. Ambos harán esto a través del IUserRepository pero es una cuestión de gusto y tal vez cómo reflejará el dominio real.

Finalmente ... Espero que entiendan algo de lo de arriba ... de todos modos. Lo más importante es comenzar a agregar métodos de "verbos" a las entidades y hacer que colaboren. También puede tener objetos en su dominio que no se conservan, solo existen para mediar entre varias entidades de dominio y pueden hospedar algoritmos, etc.

+0

Hola, muchas gracias por todas sus excelentes ideas. Lástima que solo puedo marcar una respuesta, porque esta publicación me lleva en la dirección correcta, al igual que la de Dmitry. Solo una pregunta: si está utilizando eventos de dominio, tal vez de manera asíncrona, ¿cómo maneja la situación donde no se pudo enviar la notificación por correo electrónico? ¿Sería eso solo un manejo de excepciones personalizado en el controlador de eventos? – lasseschou

+0

Si utiliza NServiceBus como plataforma, utiliza las transacciones de MSDTC, por lo que si su código falla, el mensaje se revertirá (supongo). sin embargo, si todo funciona y usted tiene su mensaje en cola y el servidor SMTP agota el tiempo de espera debido al error del servidor de correo ... bueno. Luego debe capturar ese mensaje de error e informar al usuario sobre la falla del correo. Pero NserviceBus intentará enviar este correo electrónico 5 veces (5 por defecto). Entonces puede hacer que reporte el error a una cola de error. A continuación, puede manejar ese mensaje de error e informar al usuario sobre el error. –

+0

bien, gracias Magnus. Heja de Dinamarca – lasseschou

Cuestiones relacionadas