user11081980
user11081980

Reputation: 3289

Proper way to get aggregates in Domain Driven Design

In a DDD architecture, I have an Order aggregate root and a Product aggregate. When I want to add a Product to a specific Order, I do:

class Order
{
  void AddProduct(Product p)
  {
    this.Products = p;
  }
}

Then, OrderRepository persists the in-memory Order.Products colleciton to the database.

My question is: how can I get a specific Product for a specific Order? Given that we should not inject repositories into entities, I am not sure how to hydrate the in-memory Order.Products collection:

class Order
{
  Product GetProduct(int productID)
  {
    return this.Products.Where(x => x.ProductID == productID);
  }
}

Or is this something that belongs to the OrderRepository?

Upvotes: 1

Views: 783

Answers (2)

Eben Roux
Eben Roux

Reputation: 13256

I know that this question has an accepted answer but I'll give my opinion anyway :)

An Aggregate Root (AR) should never reference another AR. It only ever uses either just the Id of the related AR or a Value Object (VO) that contains the Id of the related AR along with some optional extra data.

The interesting thing about the Order -> Product relationship is that it is a typical many-to-many relation in database terms. In the database world one would model that, typically, by creating an association (or link) table called OrderProduct and add columns for any data related to the association, such as quantity and price. However, somehow the table we all use is called OrderItem or OrderLine. We do this because it makes sense to have the association closer to the order.

I have a blog post around this that still holds somewhat true: http://www.ebenroux.co.za/design/2009/09/08/many-to-many-aggregate-roots/

In the DDD world we would then not have our Order keep a list of object references to Product instances. Instead we would create a VO called OrderItem that contains only the ProductId along with the Quantity, Price, and a denormalized ProductDescription since that description may change after the order has been placed and, historically, that would change what was ordered which would be a no-no.

Just some food for thought :)

Upvotes: 3

VoiceOfUnreason
VoiceOfUnreason

Reputation: 57249

class Order
{
    void AddProduct(Product p)
    {
        this.Products = p;
    }
}

This model is probably wrong.

(1) If the state of Product needs to satisfy the business invariant in Order, then Product should be an entity within the Order aggregate but not also a separate aggregate of its own.

On the other hand, if Order doesn't need to know anything about the state of Product to satisfy its own invariants, then the Products collection should not contain products, but something else (probably Id<Product>)

Hint: does it make any sense to edit a Product by changing the Order ?

(2) Most discussions of Order aggregates reach the conclusion that the order holds a collection of OrderItems (note: not aggregates), where each OrderItem holds a reference to the Product aggregate.

Shopping carts are a fairly common example case in DDD discussions

Upvotes: 3

Related Questions