I am attempting to model a set of entities and am seeking best practices on determining if a domain object is a candidate for an aggregate root or as a list of child entities under another aggregate root.
I have a Game
object that represents a played game of a generic sport. The Game
aggregate currently has a list of child entities titled GamePlayers
. These GamePlayer
entities also have a list of child entities titled GamePlayerHits
which record where a player was hit during a game of this generic sport.
Methods exist on the Game
aggregate such as AddPlayer()
and RemovePlayer()
. The child entities under GamePlayer
of type GamePlayerHit
also have methods such as AddHit()
.
The problem I'm facing is whether GamePlayers
should be an aggregate root of its own with a reference to the Game
entity. A Game
can technically exist without a list of GamePlayers
initially, at some later stage it may get populated. The Game
could also be deleted or cancelled without any players ever being registered. This leads me to believe that GamePlayers
should be it's own aggregate based on examples I've see online.
This is the example where I began to question the best practices surrounding this subject:
https://github.com/kgrzybek/modular-monolith-with-ddd
Here, the Meetings
aggregate has a list of child MeetingAttendee
entities however the Meetings commenting feature MeetingComment
is it's own aggregate with a reference back to the Meeting
it belongs to. For ease of referencing:
The Meeting class with MeetingAttendee child entities
The MeetingComment class as it's own aggregrate
In this example why is MeetingComment
not a list of child entities under the Meeting
aggregate with a suitable AddComment()
and DeleteComment()
methods on the main aggregate? My assumption is either comments are optional and are not required to validate the aggregrate and/or these entities need referencing elsewhere via an identifier/key which wouldn't be possible as being a member of a child collection.
How does one determine taking in to account my example above regarding games which is the correct way to models these aggregates and entities? What determines if entities should exist as child entities or as a separate aggregate root?
CodePudding user response:
How does one determine taking in to account my example above regarding games which is the correct way to models these aggregates and entities? What determines if entities should exist as child entities or as a separate aggregate root?
The existence of a domain constraints connecting information.
If all we needed was to capture a bunch of information provided to us, we wouldn't need a domain model; a database is more than adequate for data capture.
We introduce the domain model because either (a) we need to ensure that new information is compatible with information that we have already captured and/or (b) there are derived values that we want to compute locally (ex: because computing them locally reduces the error rate).
Rule: If we have a constraint that must always hold, and that constraint spans multiple pieces of information, then that information must all be contained within a single "aggregate".
The justification for the rule: information subject to a constraint like this must be changed atomically. In other words, we want all of this information to be part of a single unit for the purpose of data changes. Therefore, the domain entities that manage this information must all be part of the same "aggregate".
In the cargo shipping example taken from the blue book (Evans, 2003), we have a constraint on the misdirected property of a shipment: it must be true if the most recently recorded handling event for a container is inconsistent with the most recently assigned itinerary. It follows that these three pieces of information must be stored atomically (otherwise we risk inconsistencies in our stored data), and therefore that the domain entities that manage this information must all be part of the same aggregate.
I tend to imagine a graph - the data are nodes, and each constraint implies edges connecting the data subject to that constraint. Our rule then becomes "every graph must be contained entirely within a single aggregate".
A single aggregate can contain more than one of these graphs, but in doing that you are also introducing contention that is not inherent to the domain -- this is one of the forms of "accidental complexity".