2009-03-16 8 views
7

Parece que me he confundido un poco con todo este asunto de DDD \ LinqToSql. Estoy construyendo un sistema usando POCOS y linq a sql y tengo repositorios para las raíces agregadas. Entonces, por ejemplo, si tuviera las clases Order-> OrderLine, tiene un repositorio para Order pero no OrderLine, ya que Order es la raíz del agregado. El repositorio tiene el método de eliminación para eliminar el pedido, pero ¿cómo se eliminan OrderLines? Habría pensado que tenía un método en Order llamado RemoveOrderLine que eliminó la línea de la colección OrderLines pero también necesita eliminar OrderLine de la tabla subyacente l2s. Como no hay un repositorio de OrderLine, ¿cómo se supone que debes hacerlo?Diseño controlado por el dominio (Linq a SQL): ¿cómo se eliminan las partes de un agregado?

¿Tal vez tenga repositorios públicos especializados para consultar las raíces y repositorios genéricos internos que los objetos de dominio realmente usan para eliminar cosas dentro de los agregados?

public class OrderRepository : Repository<Order> { 
    public Order GetOrderByWhatever(); 
} 

public class Order { 
    public List<OrderLines> Lines {get; set;} //Will return a readonly list 
    public RemoveLine(OrderLine line) { 
     Lines.Remove(line); 
     //************* NOW WHAT? *************// 
     //(new Repository<OrderLine>(uow)).Delete(line) Perhaps?? 
     // But now we have to pass in the UOW and object is not persistent ignorant. AAGH! 
    } 
} 

Me gustaría saber lo que otras personas han hecho lo que puedo ser el único que lucha con esto .... espero .... Gracias

Respuesta

2

Se llama a la RemoveOrderLine en la Orden que llame a la lógica relacionada. Esto no incluye hacer cambios en la versión persistente de la misma.

Más tarde llama al método Guardar/Actualizar en el repositorio, que recibe el pedido modificado. El reto específico se convierte en saber qué ha cambiado en el objeto de dominio, lo que hay varias opciones (Estoy seguro de que hay más de los que yo lista):

  • tienen el objeto de dominio realizar un seguimiento de los cambios, los cuales incluiría hacer un seguimiento de que x necesita ser eliminado de las líneas de orden. Algo similar al seguimiento de la entidad también podría tenerse en cuenta.
  • Cargue la versión persistente. Tener un código en el repositorio que reconozca las diferencias entre la versión persistente y la versión en memoria, y ejecutar los cambios.
  • Cargue la versión persistente. Tener código en el agregado raíz, que le da las diferencias dado un agregado raíz original.
+0

Supongo que esta expansión significa que no está utilizando un ORM directamente en las entidades de su dominio. Esperaba poder evitar el trabajo extra que describes arriba, al tener linq a sql automáticamente persistir los cambios que realizo en el objeto de dominio ... –

+0

y, ese es el verdadero problema. He estado haciendo lo anterior. Veo comentarios todo el tiempo en Nhibernate apoyando escenarios más avanzados, pero no he visto cómo funciona con este tipo de escenario (que no es solo el uso de POCO) – eglasius

+0

Gracias; aunque no terriblemente ideal (falla de LTS, no es tu respuesta) esto parece confirmar mis propias corazonadas sobre cómo necesitaría hacer esto ... – Funka

1

En primer lugar, debe exponer interfaces para obtener referencias a su raíz de agregado (es decir, orden()). Utilice el patrón de fábrica para volver a generar una nueva instancia de la raíz agregada (es decir, orden()).

Dicho esto, los métodos en sus contros de Raíz agregada tienen acceso a sus objetos relacionados, no a sí mismo. Además, nunca exponga un tipo complejo como público en las raíces agregadas (es decir, la colección IList Lines() que indicó en el ejemplo). Esto infringe la ley de decremeter (sp ck), que dice que no puede "Dot Walk" su camino a métodos, como Order.Lines.Add().

Y también, viola la regla que permite al cliente acceder a una referencia a un objeto interno en una raíz agregada. Raíces agregadas can devuelve una referencia de un objeto interno. Siempre y cuando el cliente externo no tenga permiso para mantener una referencia a ese objeto. Es decir, su "OrderLine" pasa a RemoveLine(). No puede permitir que el cliente externo controle el estado interno de su modelo (es decir, Order() y sus OrderLines()). Por lo tanto, debe esperar que OrderLine sea una nueva instancia para actuar en consecuencia.

public interface IOrderRepository 
{ 
    Order GetOrderByWhatever(); 
} 

internal interface IOrderLineRepository 
{ 
    OrderLines GetOrderLines(); 
    void RemoveOrderLine(OrderLine line); 
} 

public class Order 
{ 
    private IOrderRepository orderRepository; 
    private IOrderLineRepository orderLineRepository; 
    internal Order() 
    { 
    // constructors should be not be exposed in your model. 
    // Use the Factory method to construct your complex Aggregate 
    // Roots. And/or use a container factory, like Castle Windsor 
    orderRepository = 
      ComponentFactory.GetInstanceOf<IOrderRepository>(); 
    orderLineRepository = 
      ComponentFactory.GetInstanceOf<IOrderLineRepository>(); 
    } 
    // you are allowed to expose this Lines property within your domain. 
    internal IList<OrderLines> Lines { get; set; } 
    public RemoveOrderLine(OrderLine line) 
    { 
    if (this.Lines.Exists(line)) 
    { 
     orderLineRepository.RemoveOrderLine(line); 
    } 
    } 
} 

no olvide su fábrica para crear nuevas instancias de la Orden():

public class OrderFactory 
{ 
    public Order CreateComponent(Type type) 
    { 
    // Create your new Order.Lines() here, if need be. 
    // Then, create an instance of your Order() type. 
    } 
} 

Su cliente externo tiene el derecho de acceder al IOrderLinesRepository directamente, a través de la interfaz para obtener una referencia de un objeto de valor dentro de su raíz agregada. Pero, trato de bloquear eso forzando mis referencias fuera de los métodos de la Raíz Agregada. Por lo tanto, podría marcar el IOrderLineRepository anterior como interno para que no quede expuesto.

En realidad, agrupo todas mis creaciones de raíz agregada en varias fábricas. No me gustó el enfoque de "Algunas raíces agregadas tendrán fábricas para tipos complejos, otras no". Es mucho más fácil tener la misma lógica seguida a lo largo del modelado del dominio. "Oh, entonces Sales() es una raíz agregada como Order(). También debe haber una fábrica para eso".

Una nota final es que si tiene una combinación, es decir, SalesOrder(), que utiliza dos modelos de Sales() y Order(), usaría un Servicio para crear y actuar en esa instancia de SalesOrder() como ninguno Sales() or Order() Aggregate Roots, ni sus repositorios o fábricas, poseen control sobre la entidad SalesOrder().

Recomiendo encarecidamente this free book por Abel Avram y Floyd Marinescu en Domain Drive Design (DDD) ya que responde directamente sus preguntas, en una página grande de 100 páginas de shrot. Junto con cómo desacoplar más sus entidades de dominio en módulos y tal.

Editar: añadido más código

+0

Gracias por la respuesta.Sin embargo, no estoy seguro de que el comportamiento de una línea de pedido esté expuesto a través de los métodos de la orden. Parece que GetOrderLineVatAmount debe estar en línea, de lo contrario, debe pasar la línea a la función en la orden. Además, ¿por qué Order tendría una referencia a OrderRepos? –

+0

Todo depende de cómo vea una "Línea de pedido". El código anterior refleja un enfoque de "Objeto de valor". Un objeto de valor es diferente de una entidad en la forma en que no tiene una identidad. ¿Cada OrderLine tiene una identidad? En caso afirmativo, ¿es realmente así si lo piensas? – eduncan911

+0

OrderLine.LineNumber OrderLine.Description OrderLine.Cost OrderLine.PartNumber La definición de un objeto de valor aquí no está en una identidad; pero, la suma de sus valores. Pero como indiqué anteriormente en la respuesta, puede exponer IOrderLineRepository() y su cliente remoto tiene derechos de acceso. – eduncan911

0

Como seguimiento .... he pasado a utilizar nhibernate (en lugar de un enlace a SQL), pero, en efecto, que no necesita los repositorios para el OrderLine. Si solo quita OrderLine de la colección en Order, simplemente eliminará OrderLine de la base de datos (suponiendo que haya hecho su asignación correctamente). Como estoy intercambiando repositorios en memoria, si desea buscar una línea de orden particular (sin conocer el orden padre) puede escribir una consulta linq a nhibernate que vincule orden con orderline donde orderlineid = el valor. De esa manera funciona cuando consulta desde el DB y desde en la memoria. Bueno, ahí lo tienes ...

+0

Bummer, Realmente esperaba que alguien respondiera mejor (¡muy excelente y muy pertinente!) Sin tener que abandonar Linq en Sql para poder hacerlo. – Funka

1

Después de luchar con este problema exacto, he encontrado la solución. Después de observar lo que el diseñador genera con l2sl, me di cuenta de que la solución está en las asociaciones bidireccionales entre orden y orden. Una orden tiene muchas líneas de pedido y una línea de pedido tiene una sola orden. La solución es usar asociaciones bidireccionales y un atributo de mapeo llamado DeleteOnNull (que puedes buscar en google para obtener información completa). Lo último que me faltaba era que la clase de entidad necesita registrarse para agregar y quitar eventos del conjunto de entidades l2s. En estos manejadores, debe configurar la asociación de orden en la línea de orden para que sea nula. Puede ver un ejemplo de esto si observa algún código que genere el diseñador de l2s.

Sé que esto es frustrante, pero después de días de luchar con él, lo tengo funcionando.

Cuestiones relacionadas