Search code examples
aggregatedomain-driven-design

Aggregate root referencing a child of a different aggregate root in DDD


I am very new to DDD, and run into a trouble modeling something. I did some research but could only find the opposite question (referencing an aggregate root from children).

In my model, I have an aggregate root called Race with a collection Rewards (that are received at different steps of the race, the reward basically contain the prize name). Rewards is therefore a child entity of the aggregate root.

I also have another aggregate root called Received reward. In my current design, the "received reward" refer to the reward by its ID, but as how I understand it, this is breaking the rules of DDD as it references a child entity of a different aggregate, rather than another root.

My first intuition would have been to actually remove the reference to Reward in the Received reward root and actually copy the prize name into the received reward (which kinda makes sense ; if the organizers later on decide to change the rewards in a race, what I have received in the past should still keep the old information). This would indeed solve the issue, but I am not sure if this is actually the correct solution, or the correct way of modeling this domain.

DDD is really a huge mental change compared to how I thought things. I really love how it helps to improve the architecture so far just by following those rules, but some things still not make full sense!

Thanks!


Solution

  • You should probably let go, at least a little bit, of the idea that Domain Driven Design has "rules" -- what it has are "patterns", and we do expect to adapt patterns in response to the balance of forces in our context.

    Copying information from one data model to another might be fine in some cases, where as in others we'd prefer to only copy a reference to it.

    Part of the point of working in domains: we can give extra attention to those domains that make up our competitive advantage. If the handling of rewards is a important differentiator, then we have a sit down and really think about what it means that this data here has a reference to some temporal data over there, and how do we want change-over-time to work.

    Often "really think about" can mean meetings with domain/subject matter experts, and having deep conversations about what the real requirements are today, and how those requirements change over time, so that you can find better ways to model that domain in code.


    From my perspective, there are fundamentally two different answers - either our aggregate stores a copy of the data that we need in the future, or it stores a copy of the arguments we need to get the data in the future.

    In the copy-the-arguments case, the arguments can include a clock value (timestamp, version, whatever) so that we can retrieve the entry we want from a history of values.

    When the copy-the-data approach is right, you are a lot happier, because copy-the-data is a lot easier to reason about than copy-the-arguments (because of the effect of change, and you may acquire accidental coupling that slows you down if your design reviews are not sufficiently vigilant).

    But when copy-the-data isn't the right approach, well then it isn't, and implementing the right thing means solving the problems that copy-the-data allows you to ignore.