问题描述:

Let's say you have an order as an aggregate root. An order contains one or more line items.

It is my understanding that it's the repository's responsibility to instantiate an order object when asked.

The line items can be loaded at the time of the order object's creation (eager loaded), or the line item collection can be populated when it is accessed by the client code (lazy loaded).

If we are using eager loading, it's seems that the repository code would take responsibility with hydrating the line items when the order is created.

However if we are using lazy loading, how is the repository called when the LineItems collection is accessed without creating a dependency on the repository from the order domain class?

网友答案:

Main problem is in Repository's ability to get only aggregate roots (presenting aggregates), thus you cannot use Repository to get line items. This can lead to aggregate encapsulation violation.

I propose something like:

//Domain level:

public interface IOrderItemList {

   IEnumerable<OrderItem> GetItems();

}

public class Order {

    private IOrderItemList _orderItems;

    public IEnumerable<OrderItem> OrderItems 
          { get { return _orderItems.GetItems() } };

    public Order(IOrderItemList orderItems) 
    {
        _orderItems = orderItems;
    }
}

public class OrderItemList : IOrderItemList
{
    private IList<OrderItem> _orderItems;

    public IEnumerable<OrderItem> GetItems() {
        return _orderItems; //or another logic
    }

    //other implementation details
}

//Data level

public class OrderItemListProxy : IOrderItemList
{
    //link to 'real' object
    private OrderItemList _orderItemList;

    private int _orderId;
    //alternatively:
    //private OrderEntity _orderEntity;

    //ORM context
    private DbContext _context;

    public OrderItemListProxy(int orderId, DbContext context)
    {
       _orderId = orderId;
       _context = context;
    }

    public IEnumerable<OrderItem> GetItems() {
        if (_orderItemList == null) 
        {
            var orderItemEntities = DbContext.Orders
              .Single(order => order.Id == _orderId).OrderItems;

            var orderItems = orderItemEntites.Select(...);
            //alternatively: use factory to create OrderItem from OrderItemEntity
            _orderItemList = new OrderItemList(orderItems);
        }
        return _orderItemList.GetItems();
    }

}

public class OrderRepository
{
   //ORM context
   private DbContext _context;

    Order GetOrder(int id)
    {
        var orderEntity = _context.Single(order => order.Id == id);
        var order = new Order(new OrderItemListProxy(id, _context))
        //alternatively:
        //var order = new Order(new OrderItemListProxy(orderEntity, _context))
        ...
        //init other fields
        ...
    }
    //other methods
    ...
}

Most important here is that IOrderItemList corresponds to domain layer, but OrderItemListProxy corresponds to data layer.

Finally,

  1. You may use IList<OrderItem> instead of custom IOrderItemList or another appropriate interface.
  2. Proxy implementation may differ.
  3. I don't provide best practicies for using db context, it may depend on technologies you use.
相关阅读:
Top