Repository Pattern without an ORM

后端 未结 3 1265
清酒与你
清酒与你 2021-02-05 05:46

I am using repository pattern in a .NET C# application that does not use an ORM. However the issue I am having is how to fill One-to-many List properties of an entity. e.g. if a

相关标签:
3条回答
  • 2021-02-05 06:14

    1) Should I load the Orders list within the GetCustomerById method?

    It's probably a good idea to separate the order mapping code from the customer mapping code. If you're writing your data access code by hand, calling that mapping module from the GetCustomerById method is your best option.

    2) What if the Order itself has another list property and so on?

    The logic to put all those together has to live somewhere; the related aggregate repository is as good a place as any.

    3) What if I want to do lazy loading? Where would I put the code to load the Orders property in customer? Inside the Orders property get{} accessor? But then I would have to inject repository into the domain entity? which I don't think is the right solution.

    The best solution I've seen is to make your repository return subclassed domain entities (using something like Castle DynamicProxy) - that lets you maintain persistence ignorance in your domain model.

    0 讨论(0)
  • 2021-02-05 06:17

    Another possible answer is to create a new Proxy object that inherits from Customer, call it CustomerProxy, and handle the lazy load there. All this is pseudo-code, so it's to give you an idea, not just copy and paste it for use.

    Example:

    public class Customer
    {
        public id {get; set;}
        public name {get; set;}
        etc...
        public virtual IList<Order> Orders {get; protected set;}
    }
    

    here is the Customer "proxy" class... this class does not live in the business layer, but in the Data Layer along with your Context and Data Mappers. Note that any collections you want to make lazy-load you should declare as virtual (I believe EF 4.0 also requires you to make props virtual, as if spins up proxy classes at runtime on pure POCO's so the Context can keep track of changes)

    internal sealed class CustomerProxy : Customer
    {
       private bool _ordersLoaded = false;
        public override IList<Order> Orders
        {
            get
            {
                IList<Order> orders = new List<Order>();
                if (!_ordersLoaded)
                {
                    //assuming you are using mappers to translate entities to db and back
                    //mappers also live in the data layer
                    CustomerDataMapper mapper = new CustomerDataMapper();
                    orders = mapper.GetOrdersByCustomerID(this.ID);
                    _ordersLoaded = true;
    
                    // Cache Cases for later use of the instance
                    base.Orders = orders;
                }
                else
                {
                    orders = base.Orders;
                }
                return orders;
            }
       }
    }
    

    So, in this case, our entity object, Customer is still free from database/datamapper code calls, which is what we want... "pure" POCO's. You've delegated the lazy-load to the proxy object which lives in the Data layer, and does instantiate data mappers and make calls.

    there is one drawback to this approach, which is calling client code can't override the lazy load... it's either on or off. So it's up to you in your particular usage circumstance. If you know maybe 75% of the time you'll always needs the Orders of a Customer, than lazy-load is probably not the best bet. It would be better for your CustomerDataMapper to populate that collection at the time you get a Customer entity.

    Again, I think NHibernate and EF 4.0 both allow you to change lazy-loading characteristics at runtime, so, as per usual, it makes sense to use an ORM, b/c a lot of functionality is provided for you.

    If you don't use Orders that often, then use a lazy-load to populate the Orders collection.

    I hope that this is "right", and is a way of accomplishing lazy-load the correct way for Domain Model designs. I'm still a newbie at this stuff...

    Mike

    0 讨论(0)
  • 2021-02-05 06:21

    can I do DDD without ORM ?

    Yes, but an ORM simplifies things.

    To be honest I think your problem isn't to do with whether you need an ORM or not - it's that you are thinking too much about the data rather than behaviour which is the key for success with DDD. In terms of the data model, most entities will have associations to most another entities in some form, and from this perspective you could traverse all around the model. This is what it looks like with your customer and orders and perhaps why you think you need lazy loading. But you need to use aggregates to break these relationships up into behavioural groups.

    For example why have you modelled the customer aggregate to have a list of order? If the answer is "because a customer can have orders" then I'm not sure you're in the mindset of DDD.

    What behaviour is there that requires the customer to have a list of orders? When you give more thought to the behaviour of your domain (i.e. what data is required at what point) you can model your aggregates based around use cases and things become much clearer and much easier as you are only change tracking for a small set of objects in the aggregate boundary.

    I suspect that Customer should be a separate aggregate without a list of orders, and Order should be an aggregate with a list of order lines. If you need to perform operations on each order for a customer then use orderRepository.GetOrdersForCustomer(customerID); make your changes then use orderRespository.Save(order);

    Regarding change tracking without an ORM there are various ways you can do this, for example the order aggregate could raise events that the order repository is listening to for deleted order lines. These could then be deleted when the unit of work completed. Or a slightly less elegant way is to maintain deleted lists, i.e. order.DeletedOrderLines which your repository can obviously read.

    To Summarise:

    • I think you need to think more about behaviour than data
    • ORM's make life easier for change tracking, but you can do it without one and you can definitely do DDD without one.

    EDIT in response to comment:

    I don't think I'd implement lazy loading for order lines. What operations are you likely to perform on the order without needing the order lines? Not many I suspect.

    However, I'm not one to be confined to the 'rules' of DDD when it doesn't seem to make sense, so... If in the unlikely scenario that there are a number of operations performed on the order object that didn't require the order lines to be populated AND there are often a large number of order lines associated to an order (both would have to be true for me to consider it an issue) then I'd do this:

    Have this private field in the order object:

    private Func<Guid, IList<OrderLine>> _lazilyGetOrderLines;
    

    Which would be passed by the order repository to the order on creation:

    Order order = new Order(this.GetOrderLines);
    

    Where this is a private method on the OrderRepository:

    private IList<OrderLine> GetOrderLines(Guid orderId)
    {
        //DAL Code here
    
    }
    

    Then in the order lines property could look like:

    public IEnumberable<OrderLine> OrderLines
    { 
        get 
        {
             if (_orderLines == null)
                _orderLines = _lazilyGetOrderLines(this.OrderId);
    
             return _orderLines;
        }
    }
    

    Edit 2

    I've found this blog post which has a similar solution to mine but slightly more elegant:

    http://thinkbeforecoding.com/post/2009/02/07/Lazy-load-and-persistence-ignorance

    0 讨论(0)
提交回复
热议问题