How do you properly reference an entity inside an aggregate? For example, by the index of a list (doesn't seem smart if you re-order the list); a GUID (thought it was supposed to be local?); or some other incrementing counter?

For example:

class Cart
  public IReadOnlyList<Item> Items => _items.ToList();

  public AddItem(Item item)

  public RemoveItem(/* Item or ItemId or index (int)? */) <- Here
    _items.Remove(xxxxx); // <- And here

  private readonly List<Item> _items = new List<Item>(); // Maybe need different data structure

class Item
  // Local identity here? 

  public int Quantity { get; private set; }

  public Item(Product product)
    // use product properties needed

  // other item properties

It seems wrong for a client to have to return an List<Item> from the Cart to then use the index to remove the Item. Additionally, if I instantiate the Item outside the class prior to adding it, how do I impose a local Id (I would have a GUID if instantiating outside)?

TL;DR how to deal with local identities of entities (i.e. how local is "local")?

  • You should use a GUID to reference a nested entity. – Constantin Galbenu Nov 15 '18 at 5:28
  • DDD doesn't enforce a specific format for internal IDs, only that one should not reference a mutable nested-entity outside the Aggregate.l – Constantin Galbenu Nov 15 '18 at 6:09

A client does not add or remove an Item to/from Cart. A client commands the Cart to add/remove Product (VO) and quantity. It’s only an Item once it is in the Cart. Does that make sense? Allowing a client to hold a reference to an Item could potentially break invariants should it modify the Item from outside the Cart aggregate.

In terms of how the Cart keeps track of each Item, this is up to you. Don’t overthink it. Focus on the behavior you would like to achieve and let the data that enables it be an implementation detail. It’s really not important how the inner workings of your Cart are organized. If you want to use a GUID or counter, just do it. Don’t worry about what “seems” right. Maybe you don't even need a List<Item>. There are other ways of organizing the data.

I know that’s probably not what you want to hear, but offering a specific solution would mean bringing the focus of this discussion on the data of your system instead of its behavior. That’s not the DDD way.

  • if Product is a VO wouldn't this imply that it would not hold a SKU since that's a form of identity? – keelerjr12 Nov 14 '18 at 21:08
  • 1
    The term "Value Object" does not mean an object has no identity, rather, that it's identity is the aggregate of it's values such that two objects are the same if their values are equal (e.g. an EmailAddress). Maybe a Product only has a single SKU property? I have no clue. You are passing Product to Item in your example. For the same reason you don't want to let a client have a reference to an Item, you don't want them to hold a reference to Product either (or make it a VO). Maybe the signature to add an Item to your Cart looks like Cart.Add( string sku, int quantity ). – king-side-slide Nov 14 '18 at 21:37

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.