It's sometimes difficult to define the boundaries of your aggregate. What can work well for one part of the system, doesn't necessarily work well for another part. I think we all have the desire to make our models perfect, but as with most man made things, nothing is ever perfect. I want to present an case that deals with many-to-many (or two one-to-many) relationships in aggregates and where some complications can form.
Say we're building an online survey engine where administrators can create custom surveys for gathering data. Let me outline some simple rules to set the stage.
- Surveys are made up of questions.
- Many questions are predefined and have business rules which could affect other questions.
- A single question can exist on many surveys.
From these rules we can see that we have two main entities (Survey and Question). When we define our aggregates in the system, we try to look at the way the data is to be used in the system. In our case, we always edit a Question in the context of a Survey. We could create a single aggregate where you have a Survey entity (the aggregate root) which exposes a collection of Question entities. Doing it this way may seem simple, but there are complications.
When we modify a question, we could be changing more than one survey since a question can exists on multiple surveys. That would mean that we're modifying an aggregate outside of our working aggregate instance. This sounds dirty. That may not seem like a big deal since you're just changing text, but what if it was a different type of question (possibly multiple choice) that has business logic that affects other questions? You may make valid changes on one Survey that completely breaks another. You could say that a Survey is responsible for ensuring that any of its Questions are valid on any other Surveys that use those same Questions, but loading other aggregate instances seems dirty too. Plus there could be thousands of Surveys that share many of the same Questions. Loading the Questions in interest outside the context of their Surveys would also break the rules of the aggregate.
To me, this is a complication with many-to-many relationships and using a single aggregate. In cases like this, I'm thinking it's best to have two aggregates. One aggregate would have a Survey entity (the aggregate root) which exposes a collection of SurveyQuestions entities, which have QuestionIDs, but not an object reference to Question entity. The Question entity lives in it's own aggregate. Any changes to a Question that has rules that interact with other Questions on the same survey can be free to load those Questions to apply validation.
Having multiple aggregates is an inconvenience for client code since it really deals with surveys as a whole, but I believe you can overcome that by creating an abstraction around both of these aggregates that allows you to deal with them as a single model. I would be interested in hearing how other folks are dealing with these sorts of complications.
Technorati Tags:
DDD,
Aggregates
Chad has two great posts back to back that are related to professionalism in software development:
Programming is a Noble Profession, Be Proud, Be Strong
Automated Testing is an Ethical Matter, IMHO
Nice job Chad.