7

Soy nuevo en los modelos de dominio, POCO y DDD, por lo que todavía estoy tratando de entender algunas ideas.Al trabajar con modelos de dominio y clases POCO, ¿a dónde van las consultas?

Una de las cosas que no pude entender todavía es cómo mantener mis modelos de dominio simples y agnósticos al almacenamiento, pero aún así poder realizar algunas consultas sobre sus datos de manera enriquecedora.

Por ejemplo, supongamos que tengo una orden de entidad que tiene una colección de elementos de Ordem. Quiero obtener el artículo más barato, por cualquier razón, o tal vez una lista de artículos de pedido que no están actualmente en stock. Lo que no quiero hacer es recuperar todos los artículos de pedido del almacenamiento y filtrarlos más tarde (demasiado caros), así que quiero terminar teniendo una consulta db del tipo "SELECCIONAR ... DONDE ITEM.INSTOCK = FALSE" de alguna manera. No quiero tener esa consulta SQL en mi entidad, o cualquier variación de si eso me atara a una plataforma específica, como las consultas de NHibernate en Linq2SQL. ¿Cuál es la solución común en ese caso?

Respuesta

2

Según entiendo este estilo de diseño, encapsularía la consulta en un método de un objeto OrderItemRepository (o quizás más adecuado OrderRepository), cuya responsabilidad es hablar con el DB por un lado, y devolver objetos OrderItem en el otro lado. El repositorio oculta detalles de la base de datos de los consumidores de instancias de OrderItem.

+0

¿Habría inyectado este repositorio en la clase Order (algo que tiene un IOrderRepository como parte del constructor Order), o lo tendría fuera? Si es así, ¿qué tendrías en la clase de Orden? No estoy seguro de dónde está la regla de conocimiento/negociación de lo que es un artículo sin stock va –

+0

. Quisiera pasar el repositorio a cualquier objeto de dominio que necesite un pedido. No creo que tenga mucho sentido que cada instancia de Orden tenga una referencia a su Repositorio de origen, aunque. – Morendil

+0

@Morendil Estoy casi de acuerdo: una orden no debería saber nada sobre su repositorio, pero más allá de eso, el objeto de dominio * no * debe saber sobre * cualquier * repositorio. Si DomainObjectA necesita una instancia de DomainObjectB, algún coordinador de nivel superior debería hablar con sus repositorios y conectar cosas. –

3

Los objetos de dominio deben ser independientes del almacenamiento, debe usar el patrón Repostiory o DAO para conservar los objetos. De esa manera está imponiendo la separación de las preocupaciones, el objeto en sí mismo no debería saber cómo se almacena.

Idealmente, sería una buena idea poner construcción de consultas dentro del repositorio, aunque usaría un ORM allí.

Aquí hay definición de los Repository Pattern.

+0

Entonces, desde dentro de mi clase de orden, ¿haría algo como _orderRepository.GetOrderItemsNotInStock (this)? –

+0

No, tendría un OrderRepository de clase, que tendría una función como GetById (OrderId). El repositorio existe fuera de la clase Order, la clase de orden sería, idealmente, bastante pequeña, probablemente solo propiedades con getters y setters, así como la lógica relevante lógica relevante de lógica de negocio ( –

+0

). Aunque prefiero crear clases de servicio que realmente lleven a cabo gran parte de la lógica de negocios, pero estoy un poco confusa acerca de si las clases de servicio son parte de DDD. –

3

Entidades de Martin Fowler son las "unidades" de un dominio. Repositorios y servicios de referencia ellos, no al revés. Piénselo de esta manera: ¿lleva el DMV en su bolsillo?

OrderItem no es una raíz agregada; no debería ser accesible a través de un repositorio. Su identidad es local a Order, lo que significa que Order siempre estará dentro del alcance cuando se habla de OrderItem s.

La dificultad de encontrar un hogar para las consultas me lleva a pensar en los servicios. En este caso, representarían algo sobre un Order que es difícil para un Order saber.

declarar la intención en el proyecto de dominio:

public interface ICheapestItemService 
{ 
    OrderItem GetCheapestItem(Order order); 
} 

public interface IInventoryService 
{ 
    IEnumerable<OrderItem> GetOutOfStockItems(Order order); 
} 

declarar la aplicación en el proyecto de datos:

public class CheapestItemService : ICheapestItemService 
{ 
    private IQueryable<OrderItem> _orderItems; 

    public CheapestItemService(IQueryable<OrderItem> orderItems) 
    { 
     _orderItems = orderItems; 
    } 

    public OrderItem GetCheapestItem(Order order) 
    { 
     var itemsByPrice = 
      from item in _orderItems 
      where item.Order == order 
      orderby item.Price 
      select item; 

     return itemsByPrice.FirstOrDefault(); 
    } 
} 

public class InventoryService : IInventoryService 
{ 
    private IQueryable<OrderItem> _orderItems; 

    public InventoryService(IQueryable<OrderItem> orderItems) 
    { 
     _orderItems = orderItems; 
    } 

    public IEnumerable<OrderItem> GetOutOfStockItems(Order order) 
    { 
     return _orderItems.Where(item => item.Order == order && !item.InStock); 
    } 
} 

Este ejemplo funciona con cualquier proveedor de LINQ. Alternativamente, el proyecto de datos podría usar NHibernate's ISession y ICriteria para hacer el trabajo sucio.

+0

Estoy seguro de que me falta algo, pero cada vez que intento implementar algo como esto, _no funciona con "ningún" proveedor de LINQ. Por ejemplo, la consulta en GetCheapestOrderItem puede "verse" diferente si estuviera usando SDS, EF o L2S, especialmente si tengo que hacer uniones u otra lógica de consulta no trivial. – Daniel

+0

Esa es una preocupación general sobre las abstracciones con fugas. Estas consultas se centran en la semántica del modelo. Depende de la implementación de IQueryable traducir esas semánticas a mecánicas (la parte "mirar diferente"). –

-2

En la capa de servicio.

0

Me atrevería a decir que no tiene sentido hablar de "un pedido que solo contiene los artículos de pedido que no están en stock".Una "Orden" (supongo) representa la lista completa de lo que haya ordenado el cliente; Si está filtrando esa lista, ya no está tratando con un Pedido per se, se trata de una lista filtrada de Artículos de pedido.

Creo que la pregunta es si realmente desea tratar los pedidos como Aggregate Root, o si también desea poder extraer listas arbitrarias de artículos de pedido de su capa de acceso a datos.

Has dicho que filtrar elementos después de que hayan vuelto de la base de datos sería demasiado costoso, pero a menos que promedie cientos o miles de artículos de pedido para cada orden (o hay algo más especial sobre tratar con muchos OrderItems) puede estar tratando de optimizar prematuramente y hacer las cosas más difíciles de lo que deben ser. Creo que si puede dejar Order como raíz y filtro agregados en la lógica de su dominio, su modelo será más limpio para trabajar.

Si eso es realmente no el caso y hay que filtrar en la base de datos, entonces es posible que desee considerar la posibilidad de un repositorio de pedido separada que proporcionaría consultas como "dame todos los artículos de pedido para esta orden que no están en stock". A continuación, los devolverá como IList<OrderItem> (o IEnumerable<OrderItem>), ya que son no un pedido completo, sino más bien una colección filtrada de artículos de pedido.

Cuestiones relacionadas