Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DDD Aggregates: Entity holding identifier to Non-Root Entity in another Aggregate

I'm trying to understand Best Practise for relationships between entities and aggregates.

Imagine a design where you have a Product Aggregate, made up of of two entities:

Aggregate Root: Product

Child Entity: Sku

Where a product can have many Skus. The part numbers and names of the Skus and Product are invariant, in that changing the name of one must transactionally ensure the other is updated. Likewise, the product aggregate needs to ensure there are never duplicate Skus.

We have another Aggregate: StorageLocation. where 1 or more Skus are stored. However it's important that the StorageLocation know the specific Sku it's storing. ie. A StorageLocation in Canada should store the Sku local to that country, and not a Sku intended for the US market.

This implies to me that the StorageLocation needs to keep a reference to the Sku (as a reference to the Product Aggregate Root by itself does not provide enough information to determine the Sku being Stored).

From my readings this seems to break the principle that another Aggregate should not hold a reference to a non-root entity in another Aggregate. So question:

  • Is holding just an identifier to the Product and Sku in the StorageLocation aggregate acceptable?
  • I'm aware that a transient reference is deemed allowable, but in this instance (at least as far as I can tell) this information needs to be stored. As mentioned, storing a reference to the Product (or ProductId) is not enough.
  • Product and Skus have natural identifiers (Part Number, Sku Number). Does this provide greater flexibility for storing these values in the StorageLocation aggregate as they have meaning beyond technical implications.
  • Am I approaching this the wrong way and need to look at things differently. I often find it difficult to break out of a PK / FK mindset.

Thanks

like image 654
Steven Avatar asked Mar 09 '18 20:03

Steven


People also ask

Can an aggregate reference another aggregate?

[Evans] states that one Aggregate may hold references to the Root of other Aggregates. However, we must keep in mind that this does not place the referenced Aggregate inside the consistency boundary of the one referencing it. The reference does not cause the formation of just one whole Aggregate.

Can aggregates share entities?

As you rightly pointed out, Entity instances shouldn't be shared between aggregates, as one aggregate wouldn't be aware of changes to the entity made through another aggregate and couldn't enforce its invariants.

Can you have multiple aggregate roots?

A single Bounded Context can include many aggregate roots, or we can organise a single aggregate root into a single Bounded Context. An Order aggregate root, consists of entities such as OrderLine, Customer and Product, and value objects such as ShippingAdress, PaymentMethod etc.

Can a bounded context have multiple aggregates?

Aggregates. The stategic DDD's bounded contexts typically contain multiple aggregates. Within the aggregates, you can model your system with the tactic DDD patterns, such as Entity, Value Object, Domain Event, Service and Repository.


2 Answers

The guidance to not store a reference to a child entity is founded on good principals but I believe often causes confusion. The goal is actually that the child entity does not necessarily have a 'globally unique' identifier, and that no repositories give direct access to the child entity using such a globally unique identifier.

However, if your StorageLocation holds the globally unique identifier for the Product, as well as a possibly locally unique identifier for the SKU, then there is nothing wrong with patterns such as:

var storageLocation = _storageLocationRepository.Get(id);
var product = _productRepository.Get(storageLocation.ProductId);
product.DoSomethingToSku(storageLocation.SkuId);

The key is that by ensuring that you always obtain the product from a repository, and then interact with the child entity via the product, you are ensuring the product has the opportunity to protect it's own invariants.

So to summarise:

  • Feel free store an identifier to a child entity as long as you also store the global identifier for its aggregate root.
    • In this case, the child entity id may be locally unique or globally unique - it doesn't matter as long as the access to the child entity is via the aggregate root.
  • Never load a child entity directly from the a repository and call methods on it - as then the aggregate root has no chance to protect its invariants.
    • In fact, you should never even have a repository for a child entity
like image 181
Chris Simon Avatar answered Oct 19 '22 06:10

Chris Simon


Is holding just an identifier to the Product and Sku in the StorageLocation aggregate acceptable?

Yes, because identifiers are value objects in DDD, storing the identifier in the StorageLocation aggregate does not break the rule of holding a reference to another aggregate's child entity because the value object is just a value object and no longer has any direct association with the originating aggregate.

I'm aware that a transient reference is deemed allowable, but in this instance (at least as far as I can tell) this information needs to be stored. As mentioned, storing a reference to the Product (or ProductId) is not enough.

The dehydration of the StorageLocation aggregate root back to the db should include everything that needs to be persisted. The infrastructure layer determines how your domain objects are stored in the physical repository and could be a completely different model design than your domain model depending on the concerns with the persistence technology.

Product and Skus have natural identifiers (Part Number, Sku Number). Does this provide greater flexibility for storing these values in the StorageLocation aggregate as they have meaning beyond technical implications.

There is nothing to compare "greater flexibility" to because you would only use natural identifiers or transient references for referring to entities inside the Product aggregate from the StorageLocation aggregate.

Am I approaching this the wrong way and need to look at things differently. I often find it difficult to break out of a PK / FK mindset.

Keep in mind that the persistence layer concerns should not entangle with the domain model and that the intent is to make the domain model clear and synonymous with current business rules.

like image 30
Aaron Hawkins Avatar answered Oct 19 '22 06:10

Aaron Hawkins