In the Pluralsight course Domain-Driven Design Fundamentals, there's an example of how the design of an Aggregate takes shape. The example involves patient Appointments in a clinic. The appointment has relations e.g. to a doctor, or an exam room. And the example is preceded by an analysis concluding that Appointment should not be an aggregate root to Doctor and ExamRoom. And one step in the design evolution is going from the Appointment having object references to Doctor and ExamRoom objects, to holding primitive id's of these other entities, DoctorId and ExamRoomId. They motivate this change by saying: "By simply including the ids of the related concepts rather than object references we're able to ensure that creating and changing Appointment has a minimal impact on our system when we persist our Appointment"
My first question: Is this a common design pattern? If I understand it correctly it would generalise to something like: If object A relates to object B, but operating on A should never entail making changes on B, then reference it by its id, not by B itself. Is this something you would recommend?
My second question: Has this anything to do with DDD? I mean the fact that Appointment should not be an aggregate root of doctor, doesn't mean that it can't hold object references to it, or am I missing something?
[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.
A DDD aggregate is a cluster of domain objects that can be treated as a single unit. An example may be an order and its line-items, these will be separate objects, but it's useful to treat the order (together with its line items) as a single aggregate.
Aggregation is the boundary of consistency and the encapsulation of closely related objects. Aggregation encapsulates entity objects and value objects and takes the most important entity object as the aggregate root.
An aggregate is a collection of one or more related entities (and possibly value objects). Each aggregate has a single root entity, referred to as the aggregate root. The aggregate root is responsible for controlling access to all of the members of its aggregate.
The root is the only member of the AGGREGATE that outside objects are allowed to hold references to
If you use some ORM like Hibernate, you'll maybe have to deal with lazy loading in order to cope with deeply linked object structures that have object references. Some people consider lazy loading an anti pattern.
Have a look at this QA to better grasp the concept of aggregates. Personally I'm convinced that clearly defined aggregate boundaries improve your architecture.
UPDATE: Vaughn Vernon talks about rules that spell out the current consensus view of DDD leaders on the style of aggregates (see part II):
[DDD] 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.
He continues:
If you are modifying multiple instances in a single transaction, it may be a strong indication that your consistency boundaries are wrong. If so, it is possibly a missed modeling opportunity; a concept of your ubiquitous language has not yet been discovered although it is waving its hands and shouting at you (see Part I).
In my understanding the Appointment should not hold direct object references to other aggregate roots like the Doctor.
Yes, Appointment
should not be an AR for Doctor
and ExamRoom
because that doesn't form the best boundaries for the business being modelled.
Creating an appointment for example, shouldn't take care of validating a whole model that includes the doctor and the exam room (business invariants/rules).
The other two entities would be better off being modelled as separate ARs of themselves. This decouples the system and allows the three models to operate separately, allowing much better concurrency when dealing with all three models at the same time, for example by 3 different users working concurrently on the 3 different aspects (ie. models).
Remember when they asked the customer if more than one person will schedule appointments at one time? She said no, but it's not excluded that at one time in the near future, they will want to allow that as well.
That gave Steve and Julie motive to start simple, with an initial version of the domain model that modelled everything in one huge aggregate... perhaps on purpose, so they could later make two important points for DDD: refactoring and designing small aggregates.
So to answer your first question, it would be better for Appointment
(an AR) to hold an Id as a reference to Doctor
(as an AR), and not an object reference.
While holding an object reference could seem fine at first, because after all Doctor
, in this case, is an AR and is encapsulated and responsible for its own invariants, it can easily end up that the doctor object is no longer a reference to an external entity but an entity that is in whole part of the appointment AR, because it is easy to start calling methods on it and linking internal (Appointment
) logic to that, expecting some form of state consistency.
What happens then if another AR also holds a reference to the same doctor and starts doing the same, thinking it's the only AR responsible for the state of that doctor? Well you'd have a tightly coupled, broken, inconsistent model.
But in one situation, Appointment
(AR) can hold an object reference to Doctor
if the doctor is modelled as internal to (ie. a part of) the appointment aggregate. But then nothing outside can (and should not!) reference the doctor!
To answer your second question, which should probably be obvious by now: of course it has everything to do with (proper) DDD. Operating on Appointment
should not also operate on Doctor
because this would cross aggregate boundaries (you're saving multiple aggregate roots in one transaction). You should probably think then that maybe the current model is not the best one, or perhaps there is some hidden business rule that you haven't uncovered yet.
But the appointment can hold a reference to the doctor (by Id, of course). This part of your assumption was incorrect. But I guess it was obvious by now. It's part of the first question.
I saw this question while learning DDD and saw that it's from 2015 and it hasn't gotten an answer to your first comment on @mdo's answer, so I thought I should try and answer the question as well, but try and be less succint. Perhaps @mdo considered that the answer to your comment is also in his answer to your question. I can see why. DDD is confusing and hard to grasp. I'm still learning it myself. Anyway, hope I got it right... for me and everybody else reading this answer. 😅
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