Object reference vs reference by Id in DDD Aggregates
Asked Answered
A

2

23

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?

Alvey answered 5/9, 2015 at 21:4 Comment(1)
had this same question before and the three series aggregate design of Vaughn shed some light. But Im revisiting the topic to know alternatives and when is it ok to break the rule :)Michaeline
C
10
  1. I think this is a common design pattern, at least in the DDD universe. Evans says in DDD:

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.

  1. IF you implement aggregate boundaries, your appointment type will most likely not have direct object references to a doctor.

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.

Convexoconcave answered 6/9, 2015 at 23:6 Comment(2)
Thanks. I get what you mean regarding lazy loading. Even though my feeling is that in most cases object references won't necessarily require lazy loading. Regarding the Evans quote. I totally get that. And correct me if I'm wrong but that's not really to the point I think. My question was regarding if appointment could hold references to outside objects, not if outside objects could hold it. Your quote addresses the latter, which I am ok with. But if e.g. Doctor is an Aggregate root, my understanding is that it's fine for Appointment to hold a reference that Doctor.Alvey
but on presentation, you always need the name of doctor and room. I just feel having extra read model is a lot of work. any suggestion on that?Michaeline
E
4

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. 😅

Ensconce answered 12/4, 2021 at 8:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.