2008-10-22 17 views
20

Parece que la decisión de hacer que sus objetos sean plenamente conscientes de sus funciones dentro del sistema, y ​​aún así evitar tener demasiadas dependencias dentro del modelo de dominio en la base de datos, y las capas de servicio?¿Cómo evitar los modelos de dominio anémico y mantener la separación de las preocupaciones?

Por ejemplo: supongamos que tengo una entidad con un historial de revisión y varias "tablas de búsqueda" a las que hace referencia, su objeto entidad debe tener métodos para obtener detalles de algunas de las tablas de búsqueda, ya sea proporcionar acceso a las filas de la tabla de búsqueda o delegar métodos en ellas, pero para hacerlo depende de la capa de la base de datos para leer los datos de esas filas. Además, cuando se guarda la entidad, necesita saber no solo cómo guardarse a sí mismo, sino también guardar entradas en el historial de revisiones. ¿Es necesario pasar referencias a docenas de diferentes objetos de capa de datos y objetos de servicio al objeto modelo? Esto parece que hace que la lógica sea mucho más compleja de entender que el simple paso de modelos delgados hacia los objetos de capa de servicio, pero he escuchado a muchos "sabios" recomendar este tipo de estructura.

Respuesta

19

Muy buena pregunta. He pasado bastante tiempo pensando en esos temas.

Demuestra gran comprensión al notar la tensión entre un modelo de dominio expresivo y la separación de preocupaciones. Esto es muy parecido a la tensión en la pregunta que hice sobre Tell Don't Ask and Single Responsibility Principle.

Aquí está mi punto de vista sobre el tema.

Un modelo de dominio es anémico porque no contiene lógica de dominio. Otros objetos obtienen y configuran datos usando un objeto de dominio anémico. Lo que describes no me parece lógica de dominio. Podría ser, pero en general, las tablas de búsqueda y otro lenguaje técnico son términos que significan algo para nosotros pero no necesariamente para los clientes. Si esto es incorrecto, aclare.

De todos modos, la construcción y la persistencia de los objetos de dominio no deberían estar contenidos en los propios objetos de dominio porque eso no es lógica de dominio.

Para responder a la pregunta, no, no debe inyectar un montón de objetos/conceptos que no sean de dominio, como tablas de búsqueda y otros detalles de infraestructura. Esta es una filtración de una preocupación a otra. Los patrones Factory y Repository del Domain-Driven Design son los más adecuados para mantener estas preocupaciones aparte del modelo de dominio en sí.

Pero tenga en cuenta que si no tiene ninguna lógica de dominio, terminará con objetos de dominio anémicos, es decir, bolsas de getters y setters sin cerebro, que es como some shops claim to do SOA/service layers.

Entonces, ¿cómo se obtiene lo mejor de ambos mundos? ¿Cómo enfoca sus objetos de dominio solo en la lógica del dominio, mientras mantiene la interfaz de usuario, la construcción, la persistencia, etc. fuera del camino? Te recomiendo que uses una técnica como Double Dispatch, o alguna forma de restricted method access.

Aquí hay un ejemplo de Double Dispatch. Digamos que tiene esta línea de código:

entity.saveIn(repository); 

En su pregunta, saveIn() tendría todo tipo de conocimiento sobre la capa de datos. El uso de doble Despacho, saveIn() hace esto:

repository.saveEntity(this.foo, this.bar, this.baz); 

Y el método saveEntity() del repositorio tiene todo el conocimiento de cómo ahorrar en la capa de datos, como debe ser.

Además de esta configuración, usted podría tener:

repository.save(entity); 

la que sólo llama

entity.saveIn(this); 

vuelvo a leer esto y me di cuenta de que la entidad sigue siendo delgada, ya que es simplemente despachando su persistencia en el repositorio. Pero en este caso, se supone que la entidad es delgada porque no describió ninguna otra lógica de dominio. En esta situación, podría decir "atornillar doble despacho, dar acceso".

Y sí, podría, pero la OMI expone demasiado sobre cómo se implementa su entidad, y esos accesadores son distracciones de la lógica del dominio. Creo que la única clase que debería tener y establecer es una clase cuyo nombre termina en "Accesor".

Lo resumiré pronto. Personalmente, no escribo mis entidades con métodos saveIn(), porque creo que el simple hecho de tener un método saveIn() tiende a ensuciar el objeto de dominio con distracciones. Uso el patrón de clase de amigo, el acceso privado del paquete o posiblemente el Builder pattern.

Bien, he terminado. Como dije, me obsesioné bastante con este tema.

+2

En resumen, ¿está proponiendo acceder al repositorio desde el dominio? – aaimnr

0

Pruebe el "patrón de repositorio" y "Diseño controlado por el dominio". DDD sugiere definir ciertas entidades como raíces agregadas de otros objetos. Cada Agregado está encapsulado. Las entidades son "persistencia ignorante". Todo el código relacionado con la persistencia se coloca en un objeto de repositorio que gestiona el acceso a datos para la entidad. De esta forma, no tiene que mezclar el código relacionado con la persistencia con su lógica comercial. Si está interesado en DDD, consulte el libro de eric evans.

+1

No responde la pregunta. La pregunta es "¿Debe el repositorio inyectarse en el dominio"? Si es así, rompería la separación de preocupaciones. Por otro lado, si esas acciones se realizarían por servicio, podría conducir al Modelo de dominio anémico. – aaimnr

1

"modelos delgados para objetos de capa de servicio" es lo que debe hacer cuando realmente desea escribir la capa de servicio.

ORM es lo que debe hacer cuando no desea escribir la capa de servicio.

Cuando trabaje con un ORM, sigue siendo consciente de que la navegación puede implicar una consulta, pero no se detiene en ella.

Las tablas de búsqueda pueden ser una muleta relacional que se usa cuando no hay un modelo de objeto muy completo. En lugar de cosas que hacen referencia a cosas, tienes códigos, que deben buscarse. En muchos casos, los códigos devuelven poco más que un conjunto de cadenas de bases de datos estáticas. Y los métodos relevantes terminan en lugares extraños en el software.

Sin embargo, si hay un modelo de objeto más completo, tenemos cosas de primera clase en lugar de estos valores de búsqueda degenerados.

Por ejemplo, tengo algunas transacciones comerciales que tienen uno de n diferentes "planes de tarifas" - una especie de modelo de precios. En este momento, la base de datos relacional heredada tiene el plan de tarifas como una tabla de búsqueda con un código, algunos números de precios y (a veces) una descripción.

[Todo el mundo conoce los códigos: los códigos son sagrados. Nadie está seguro de cuáles deberían ser las descripciones adecuadas. Pero ellos conocen los códigos.]

Pero en realidad, un "plan de tarifas" es un objeto que está asociado con un contrato; el plan de tarifas tiene el método que calcula el precio final. Cuando una aplicación solicita un precio al contrato, el contrato delega parte del trabajo de fijación de precios en el objeto del plan tarifario asociado.

Puede haber habido alguna consulta en la base de datos pasando a buscar el plan de tarifas al producir un precio de contrato, pero eso es incidental a la delegación de responsabilidad entre las dos clases.

1

Estoy de acuerdo con DeadBeef - ahí radica la tensión. Realmente no veo cómo un modelo de dominio es "anémico" simplemente porque no se salva a sí mismo.

Tiene que haber mucho más. es decir. Es anémico porque el servicio está haciendo todas las reglas comerciales y no la entidad de dominio.

Service(IRepository) injected 

Save(){ 

DomainEntity.DoSomething(); 
Repository.Save(DomainEntity); 

} 

'Do Something' is the business logic of the domain entity. 

**This would be anemic**: 
Service(IRepository) injected 

Save(){ 

if(DomainEntity.IsSomething) 
    DomainEntity.SetItProperty(); 
Repository.Save(DomainEntity); 

} 

Ver la diferencia heredada? Lo hago :)

Cuestiones relacionadas