2010-11-12 8 views
6

He estado leyendo un poco sobre DDD, y estoy confundido sobre cómo encajaría esto al usar un ORM como NHibernate.¿Se puede utilizar un enfoque DDD puro con NHibernate?

Ahora tengo una aplicación .NET MVC con controladores bastante "gordos", y estoy tratando de encontrar la mejor manera de arreglar eso. Mover esta lógica de negocios a la capa de modelo sería la mejor manera de hacerlo, pero no estoy seguro de cómo hacerlo.

Mi aplicación está configurada para que la sesión de NHibernate sea administrada por un HttpModule (obtiene sesión/transacción fuera de mi camino), que es utilizada por repositorios que devuelven los objetos de entidad (Think S # arp arch ... resulta una gran parte de su funcionalidad realmente duplicada en esto). Estos repositorios son utilizados por DataServices, que en este momento son solo envoltorios alrededor de los repositorios (mapeo uno a uno entre ellos, por ejemplo, UserDataService toma un UserRepository, o en realidad un repositorio). Estos DataServices en este momento solo aseguran que las anotaciones de datos que decoran las clases de entidades se verifiquen al guardar/actualizar.

De esta manera, mis entidades son realmente solo objetos de datos, pero no contienen ninguna lógica real. Si bien podría poner algunas cosas en las clases de entidad (por ejemplo, un método de "aprobación"), cuando esa acción necesita hacer algo como enviar un correo electrónico, o tocar otros objetos no relacionados, o, por ejemplo, verificar para ver si hay usuarios que tienen el mismo correo electrónico antes de aprobar, etc., entonces la entidad necesitaría acceder a otros repositorios, etc. Inyectarlos con un IoC no funcionaría con NHibernate, por lo que tendría que usar una fábrica patrón que estoy asumiendo para obtener estos. Sin embargo, no veo cómo te burlarías de esos en las pruebas.

Así que la siguiente forma más lógica de hacerlo, creo, sería esencialmente tener un servicio por controlador, y extraer todo el trabajo que se realiza actualmente en el controlador en los métodos en cada servicio. Sin embargo, creo que esto está rompiendo con la idea de DDD, ya que la lógica ya no está contenida en los objetos reales del modelo.

La otra forma de ver eso, supongo, es que cada uno de esos servicios forma un único modelo con el objeto de datos contra el que funciona (Separación de campos de almacenamiento de datos y la lógica que opera sobre él), pero solo quería para ver lo que otros están haciendo para resolver el problema del "controlador de grasa" con DDD al usar un ORM como NHibernate que funciona devolviendo objetos de datos poblados y el modelo de repositorio.

Actualizado Creo que mi problema es cómo estoy mirando esto: NHibernate parece poner los objetos de negocio (entidades) en la parte inferior de la pila, que repositorios luego actuar sobre. Los repositorios son utilizados por servicios que pueden usar repositorios múltiples y otros servicios (correo electrónico, acceso a archivos) para hacer cosas. Es decir: Aplicación> Servicios> Repositorios> Objetos comerciales

El enfoque DDD puro que estoy leyendo parece reflejar un sesgo de registro activo, donde las funciones CRUD existen en los objetos comerciales (esto lo llamo User.Delete directamente en lugar de Repository.Delete de un servicio), y el objeto de negocio real maneja la lógica de las cosas que se deben hacer en esta instancia (como enviar un correo electrónico al usuario y eliminar archivos que pertenecen al usuario, etc.). Es decir. Aplicación> (Servicios)> Objetos de negocios> Repositorios

Con NHibernate, parece que sería mejor usar el primer enfoque dado la forma en que funciona NHibernate, y estoy buscando confirmación en mi lógica. O si estoy confundido, alguna aclaración sobre cómo se supone que este enfoque en capas funciona. Según entiendo, si tengo un método de "aprobación" que actualiza el modelo de usuario, lo persiste y, por decir algo, envía un correo electrónico a algunas personas, este método debe ir al objeto de entidad de usuario, pero para permitir el IoC adecuado para que pueda Inyectar el servicio de mensajería, necesito hacer esto en mi capa de servicio en lugar de hacerlo en el objeto Usuario.

Desde el punto de vista de "IU múltiple" esto tiene sentido, ya que la lógica de hacer las cosas se saca de mi capa de interfaz de usuario (MVC) y la pongo en estos servicios ... pero básicamente solo estoy factorizando lógica a otra clase en lugar de hacerlo directamente en el controlador, y si no voy a tener alguna otra IU involucrada, entonces acabo de cambiar un "controlador de grasa" por un "servicio gordo", ya que el servicio es esencialmente va a encapsular un método por acción de controlador para hacer su trabajo.

+0

No estoy seguro de qué "enfoque DDD puro" refleja un sesgo de registro activo. Eso es decididamente diferente a todo lo que recuerdo en el libro de Eric Evans. ¿Qué fuente está utilizando que sugiere que las entidades usen ActiveRecord? – JasonTrue

+0

Por lo que vale, puede usar Castle ActiveRecord si quiere que Nhibernate use ActiveRecord en lugar de un enfoque DDD. – JasonTrue

Respuesta

4

DDD hace no tienen un sesgo Active Record. Eliminar es no un método que debe estar en una entidad (como el usuario) en DDD.

NHibernate es compatible con un enfoque DDD muy bien, debido a lo completamente divorciado que permanece de las clases de entidad.

cuando esa acción tiene que hacer algo como el envío de un correo electrónico, o tocar otros objetos no relacionados

una pieza del rompecabezas que parece que se echa en falta es Eventos de dominio. Una entidad de dominio no debe enviar un correo electrónico directamente. Debería plantearse un evento en el Dominio que haya ocurrido algún evento significativo. Implemente una clase cuyo objetivo sea enviar el correo electrónico cuando ocurra el evento y registrarlo para escuchar el Evento del Dominio.

o, por ejemplo, la comprobación para ver si existen usuarios que tienen el mismo correo electrónico antes de aprobar

Esto probablemente se debe comprobar antes presentación de la llamada "aprobar , "en lugar de en la función que hace la aprobación. Empuje la decisión hacia arriba un nivel en el código de llamada.

Así que la próxima manera más lógica de hacerlo, yo creo, sería esencialmente tiene un servicio por controlador

Esto puede funcionar, si se hace en el entendido de que el servicio es un punto de entrada para el cliente. El propósito del servicio aquí es tomar los parámetros en un DTO desde el front-end/cliente y traducir eso en llamadas de método contra una entidad para realizar la funcióncualidad deseada.

+0

No estoy pensando en términos de eventos así, no ... Así que lo que estás diciendo es que las entidades SOLO deben tener métodos que se ocupen de modificar el estado del dominio (por ejemplo, cambiar valores). Cualquier cosa que esté fuera de esto debería plantearse como un evento y los manejadores de eventos solían hacer otras cosas, como eliminar archivos y enviar correos electrónicos. –

+0

Sí, básicamente, el estado del dominio * es * lo que las entidades están a cargo. Los eventos se sienten muy naturales para implementar muchos requisitos, y tienen valor en sí mismos (capturar la intención de los cambios, la auditoría, etc.). Aunque no diría SOLAMENTE o Cualquiera. A veces es más sencillo tener una excepción a la regla. Siempre es una decisión y una decisión, pero los eventos me han ayudado a ver dónde poner la funcionalidad con más claridad en muchos casos. –

+0

¿Hay un buen foro para discutir estas cosas? Siento que voy a tener muchas más preguntas ... –

0

La respuesta corta a su pregunta es sí, de hecho, NHibernate mejora la DDD: puede enfocarse en desarrollar (y modificar) su modelo de dominio con un primer acercamiento de código, luego puede volver a adaptar fácilmente la persistencia usando NHibernate.

A medida que desarrolle su modelo de dominio después de DDD, esperaría que gran parte de la lógica de negocios que encontró en los controladores MVC probablemente debería residir en los objetos de su dominio. En mi primer intento de utilizar ASP.NET MVC, me encontré rápidamente en la misma posición que usted: controladores de grasa y un modelo de dominio anémico.

Para evitar esto, ahora estoy siguiendo el enfoque de mantener un modelo de dominio enriquecido que implementa la lógica comercial y el uso del modelo de MVC como objetos de datos esencialmente simples utilizados por mis vistas. Esto simplifica mis controladores: interactúan con mi modelo de dominio y proporcionan objetos de datos simples (desde el modelo MVC) a las vistas.

Actualizado

El enfoque DDD pura que estoy leyendo parece reflejar un sesgo Active Record ...

Para mí significa que el patrón de registro activo entidades son conscientes de su mecanismo de persistencia y una entidad mapea directamente a un registro de tabla de base de datos. Esta es una forma de usar NHibernate, p. ver Castle Active Record, sin embargo, creo que esto contamina las entidades de dominio con conocimiento de su mecanismo de persistencia. En cambio, típicamente, tendré un repositorio por aggregate root en mi modelo de dominio que implementa un repositorio abstracto. El repositorio abstracto proporciona métodos CRUD básicos tales como:

public IList<TEntity> GetAll() 
public TEntity GetById(int id) 
public void SaveOrUpdate(TEntity entity) 
public void Delete(TEntity entity) 

.. que mis repositorios de hormigón pueden complementar/ampliar.

Ver this post on The NHibernate FAQ en el que he basado muchas de mis cosas. También recuerde, NHibernate (dependiendo de cómo configure sus asignaciones) le permitirá eliminar la persistencia de un gráfico de objetos completo, es decir, su raíz agregada más todos los objetos que cuelgan de ella y una vez que haya terminado de trabajar con ella, puede guardar en cascada a través de todo el gráfico de objetos, este ciertamente no es un registro activo.

... ya que el servicio se va a encapsular esencialmente un método por acción del controlador que hacer es un trabajo ...

todavía pienso que usted debe considerar lo que la funcionalidad que tiene actualmente en sus controladores debería, más lógicamente, implementarse dentro de los objetos de su dominio. p.ej. En su ejemplo de aprobación, creo que sería sensato que una entidad exponga un método de aprobación que hace lo que debe hacer dentro de la entidad y si, como en su ejemplo, necesita enviar correos electrónicos, delegue esto en un servicio. Los servicios deben reservarse para cross-cutting concerns. Luego, una vez que haya terminado de trabajar con sus objetos de dominio, páselos de vuelta a su repositorio para que persistan los cambios.

Un par de libros que he encontrado útil sobre estos temas son:
Domain-Driven Design by Eric Evans
Applying Domain-Driven Design and Patterns by Jimmy Nilsson

+0

Sí, uso el enfoque de ViewModel para que mi controlador también vea los datos. Mi pregunta gira en torno al hecho de que DDD parece indicar más un diseño de registro activo (por ejemplo, un usuario tiene un método de "eliminación" que atraviesa y elimina todos los archivos asociados con un usuario). Usar el patrón de repositorio empantana esto más. Ver mis notas arriba. –

+0

@Michael He actualizado mi respuesta en función de sus comentarios. – Graham

+0

@ Graham, sí, ese artículo es la base de mi sistema también. Mi problema es tratar de entender dónde se recortan las cosas.Estoy empezando a obtener algo de esto ahora, y estoy viendo formas de hacer las cosas, por ejemplo, inyectando mis requisitos en el tiempo de ejecución del método (en lugar de Form.Approve() tendría Form.Approve (currentUser, booleanSomething, etc.)) Entonces las cosas que deben suceder fuera del contexto del dominio (que son las entidades de la base de datos) pueden ser manejadas por eventos planteados. –

0

Las únicas limitaciones que NHibernate crea para las clases son todos los métodos/propiedades que deben ser virtuales y una clase debe tener un constructor predeterminado (puede ser interno o estar protegido). De lo contrario, no [editar] interferir con la estructura del objeto y puede mapear a modelos bastante complejos.

+0

¿Quiso decir "De lo contrario, ** no ** interfiere con la estructura del objeto"? –

+0

Bueno, mi pregunta sobre el método de eliminación fue que parecía que si OTRAS ACCIONES debían tener lugar en la eliminación de un objeto, el método de eliminación debería ir a la entidad. La metodología de eventos de dominio hace que MUCH me sea más limpio para cosas como enviar correos electrónicos, ahora que lo entiendo. La otra opción, por supuesto, es realizar estas acciones adicionales en las capas del servicio de aplicaciones, que pueden ser mejores para, por ejemplo, eliminar archivos relacionados cuando se elimina una entidad del almacén de persistencia. Solo me costaba entender cómo encajan los eventos no relacionados con la persistencia. –

Cuestiones relacionadas