In DDD can an aggregates invariant include a rule based on information in a another aggregate? Now I don't think so, however this causes me a problem and I don't know how to solve it.
I have an entity called Asset (equipment) which I'm modelling as the root of an aggregate. It has a list of Tags (properties) that describe things like Manufacturer, Model etc. It stores the identity of second aggregate called AssetType which has a list of TagTypes, of which some can be marked as mandatory.
Now it appears to me that one of the invariant conditions for Asset should make a reference to the associated AssetType to enforce non null values in the list of mandatory tags. But my guts are crawling with the thought of how I am going to enforce consistency.
Does this mean the aggregate should really comprise all four entities? If the root were to be AssetType and it had a list of Assets under it, it could solve my problem, however this is not going to fit very well with a core use case which has other aggregates maintaining lists of different types of Asset. Asset really has to be the root otherwise I'm going to have problems.
And AssetType can't very well go inside the Asset aggregate either. This seems just as absurd.
My guts still says Asset and AssetType are two separate aggregates, but how do I resolve the consistency problems? Or have I got my invariant wrong?
In DDD you can reference an aggregate root from another aggregate. What you cannot do is reference anything inside the other aggregate root.
In DDD, validation rules can be thought as invariants. The main responsibility of an aggregate is to enforce invariants across state changes for all the entities within that aggregate. Domain entities should always be valid entities. There are a certain number of invariants for an object that should always be true.
Aggregates are a design pattern that play a big role in domain-driven development. In many systems, the relationships between entities can become so interwoven that attempting to eager-load an entity and all of its related entities from persistence results in attempting to download the entire database.
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. The page Aggregate describes how you can create aggregates.
In this scenario, there are several methods for enforcing invariants.
First of all, consider the behaviors around the Asset
aggregate. I assume there is at least a CreateAssetCommand
and a RemoveTagCommand
. The invariants should be enforced during execution of these commands in the following way:
CreateAssetCommand
Since an asset is always associated with an asset type, an AssetTypeId
must be provided as part of this command. This ID must be obtained by the caller, possibly by looking up a specific asset type. When AssetType
is looked up, the corresponding TagType
entities can also be retrieved, the mandatory ones in particular. This will allow the caller to construct the required Tag
instances to send as part of the command. Note, it is the responsibility of the caller to provide a valid asset type and tags.
RemoveTagCommand
The handler for this command can retrieve the appropriate Asset
which stores the AssetTypeId
. Next, the handler retrieves the set of mandatory tags for the asset type and ensures that those tags aren't removed. In this case, the invariant is enforced by the handler itself.
Another way to handle these invariants is to introduce eventual consistency, if acceptable. With this approach, removal of a tag from an asset should publish a TagRemovedEvent
. A handler for this event can then verify that a mandatory tag wasn't removed. If it was, it can create a task or notification stating that an Asset is in an invalid state. Note, this assumes that it is acceptable for an asset to be in an invalid state until something is corrected.
Now to behaviors around AssetType
. One command that could compromise the integrity of the Asset
aggregate is the introduction of a new mandatory Tag
. In this case, the onyl way to ensure integrity is to create appropriate tags for each corresponding asset. Since this likely can't be done automatically, eventual consistency must be accepted until appropriate tags are provided via manual intervention.
With all these approaches, you don't have the kind of integrity you'd get with a RDMS. The responsibility of enforcing cross-aggregate invariants is delegated to command handlers, event handlers and calling code. However, in many instances, this kind of consistency is perfectly acceptable.
Take a look at Effective Aggregate Design for more on this.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With