I'm thinking about the design of a fairly simple problem, but it I would like to hear other solutions to handle it properly.
At his moment I have 2 aggregate roots:
User
: holds information about a user like display name
, linked accounts
, and a profile
, which can be a patient profile
or a care provider profile
. Such patient profile holds information like birth date
and gender
. I have a UserRepository
that takes care of getting and saving users.Screening
: holds information about health, like length and weight measurements, and all kind of calculated information such as 'short term evolution", based on those lengths and weights. I have a ScreeningRepository
that takes care of getting and saving users.So there is a Calculate()
method in Screening
, and the goal is that when a weight
and/or length
is added to the screening
, the health properties like short term evolution
are recalculated immediately, so that the screening
is always in a consistent and correct state.
The problem is dat this calculation also needs gender
and birth date
of the user, which are stored in the patient profile
.
So basically, the aggregate root screening
has a dependency on the aggregate root user
. So I'm wondering how to go about it...
According to DDD, an aggregate root should not reference another aggregate root. And also, if I would make user
a property of screening
, the ScreeningRepository
would also be responsible to dematerialize user as well, which is of course not his task.
If screening
has no reference to user
, then Calculate()
does not have all needed information. So this means I should probably move it to a domain service, which get user
and screening
as input, and do the calculation. Fine. But then, how can I make sure that when a measurement is added to the screening, the Calculate
is triggered?
The other option I was thinking about was to not make screening
an aggregate root, and make it just an aggregate with parent user
. This also allows me to better validate the screening
, because I can access the User
as well. It would solve all issues about the calculation, because I would have all information at hand, but this way, the UserRepository
would be responsible to also handle screening
, and my aggregate root becomes responsible of users
and screenings
.
At this point the last option seems to be the only one that makes it easy to fix the problem, but I would love to hear any thoughts on things since I might be missing obvious concepts.
There is no magic.
If the domain logic for Screening
requires gender and birth date, then you are going to need to get copies of those values into the aggregate. That in turn means that either (a) you pass the values in, or (b) you pass in a capability that supports querying the values.
It's often the case that an aggregate will cache a local copy of data that "belongs" to another aggregate. In which case you may need to work through what happens if that cached data needs to be invalidated (ex: what happens if we later discover a data entry error on birthdate?)