2009-07-17 33 views
10

Pido disculpas por adelantado si la gente piensa que esto ha sido golpeado hasta la muerte. Acabo de pasar las últimas horas buscando y leyendo muchos mensajes excelentes aquí en SO, pero todavía estoy confundido.DDD, repositorio y encapsulación

El origen de mi confusión es DTO contra DDD y repositorios. Quiero que mis objetos de dominio POCO tengan inteligencia, y quiero obtenerlos de los repositorios. Pero parece que tengo que violar algunas reglas de encapsulación para que funcione, y parece que puede convertir los DTO en sus cabezas.

Aquí hay un ejemplo simple: en nuestra aplicación de catálogo, una Parte podría ser un paquete que incluye una cantidad de otras partes. Entonces, tiene sentido que la Parte POCO tenga un método 'GetChildren()' que devuelve IEnumerable < Parte>. Incluso podría hacer otras cosas con la lista cuando se vaya.

Pero, ¿cómo se resuelve esa lista? Parece que el repositorio es la respuesta:

interface IPartRepository : IRepository<Part> 
{ 
    // Part LoadByID(int id); comes from IRepository<Part> 
    IEnumerable<Part> GetChildren(Part part); 
} 

Y

class Part 
{ 
    ... 
    public IEnumerable<Part> GetChildren() 
    { 
     // Might manipulate this list on the way out! 
     return partRepository.GetChildren(this); 
    } 
} 

Así que ahora los consumidores de mi catálogo, además de (correctamente) partes de carga desde el repositorio, también puede pasar por alto algunos Parte-encapsulados lógica llamando a GetChildren (parte) directamente. ¿No es malo?

He leído que los repositorios deberían servir para POCO pero que los DTO son buenos para transferir datos 'entre capas'. Se calculan muchas propiedades de piezas: los precios, por ejemplo, se calculan en base a reglas de precios complejas. Un precio ni siquiera estará en un DTO proveniente de un repositorio, por lo que parece que pasar los datos de fijación de precios a un servicio web requiere que el DTO consuma la Parte, y no al revés.

Esto ya es demasiado largo. ¿Dónde está mi cabeza desenroscada?

Respuesta

2

Un enfoque que resuelve este problema es mover la lógica a las partes secundarias mismas, es decir, cambiar la semántica de la clase para que los objetos Part se transformen cuando estén asociados con un elemento primario.

Por ejemplo, si el precio de una Part es dependiente de su padre Part, el precio se puede determinar en los tiempos siguientes (como mínimo):

  • Tras la construcción, si un padre Part (y todos los demás datos necesarios) está disponible.

  • En un método AttachToParent(Part parentPart) o en respuesta a un evento, es decir, OnAttachedToParent(Part parentPart).

  • Cuando es necesario por código de cliente (es decir, en el primer acceso de su propiedad Price).


Editar: mi respuesta original (abajo) realmente no estaba en el espíritu de DDD. Implicaba hacer que los objetos de dominio fueran contenedores simples, un diseño considerado por muchos como un antipatrón (ver Anemic Domain Model).

Una capa adicional entre el Part y IPartRepository (lo llamaré IPartService) resuelve este problema: mover GetChildren(part) en IPartService y sacarlo de Part, a continuación, hacer que el código del cliente llamada IPartService para obtener Part objetos y sus hijos en lugar de golpeando el repositorio directamente. La clase Part todavía tiene una propiedad ChildParts (o Children); simplemente no sabe cómo llenarla.

Obviamente, esto impone costos adicionales: es posible que termine escribiendo o generando una gran cantidad de código de paso a través de las llamadas al repositorio si no necesita una lógica comercial adicional en la mayoría de los casos.

+0

Interesante. Pero estoy confundido por 'mover GetChildren (parte) en IPartService y eliminarlo de la parte' y luego 'La clase Part todavía tiene una propiedad Childparts'. ¿Qué pasa si parte necesita dar masajes a sus hijos por alguna razón? – n8wrl

+1

Si el masaje debe suceder inmediatamente cuando lo recupere del repositorio, pondría esa lógica en 'IPartService.GetChildren()'. Si necesita poder modificar las partes secundarias en momentos arbitrarios, podría hacer otro método de servicio, como 'IPartService.UpdateChildPartPrices (Parte parte) .' (O ambas, podría llamar' UpdateChildPartPrices' de 'GetChildren'). –

+0

¡Gracias por tomarse el tiempo, Jeff! – n8wrl

0

La parte que falta de la ecuación es el comportamiento del objeto Parts y cómo desea trabajar con el agregado. ¿Necesita trabajar en niños individuales de cada Part hasta la enésima recursión, o solo trabaja con una "raíz" Part (es decir, aquellos sin padres) y son niños en su conjunto?

Tener una raíz agregada Part que contiene una lista de bastante genérica mecanografiadas Parts como los niños parece que no expresaría su modelo de dominio particularmente bien, pero podría hacerlo de forma recursiva y la carga perezosa cada colección infantil. Sin embargo, aún sería muy cuidadoso donde sea posible la recursión infinita.

En cuanto a su segunda preocupación, los DTO no son para transferir datos entre capas tanto como para transferir datos dentro y fuera de la capa de aplicación.

Son muy útiles si está utilizando una arquitectura orientada a servicios (menciona servicios web, pero puede ser cualquier SOA). Sus servicios consultarán sus repositorios, harán cualquier trabajo adicional y luego correlacionarán sus objetos de dominio en DTO planos para enviarlos a los clientes que lo soliciten. Los DTO deben ser simples, no contener ninguna lógica y función de aplicación específica con la intención de ser serializada.

Usa los objetos de tu dominio dentro de tu aplicación y los DTO externos.