Life inside an Aggregate Root, part 2
In part one I talked about how entities have reference their parent aggregate root. Today I will talk about how new entities are added to the aggregate. Let’s have a look at the spec again for my example training system:
A Training Programme is comprised of Skills, arranged in Skill Groups. Skill Groups can contain Sub Groups with as many levels deep as you like.
Here’s our original code for adding Skill Groups to a Training Programme:
SkillGroup subGroup = new SkillGroup("First Aid", programme);programme.Add(subGroup);// add skills to subgroup etc
There are three things wrong here.
- We’re breaking aggregate root boundaries. Skill Groups can only be constructed against one Training Programme, but can be added to as many Training Programmes as you like via the Add(SkillGroup) method.
- Anemic domain model: Training Programmes and Skill Groups are just dumb containers here. The actual logic is being performed in the application services layer.
- The application services layer knows too much about Skill Groups (how to construct them).
Rule #5: New objects inside the aggregate are created by existing objects
These sorts of problems can be avoided if we move the responsibility for creating Skill Groups to the Training Programme itself. The new keyword is a bit of a smell here — as Udi Dahan recently stated in a blog post, customers don’t just appear out of thin air. Let’s flip it around:
SkillGroup subGroup = trainingProgramme.AddSubGroup("First Aid");// add skills to subgroup etc
Now the Training Programme knows about creating and adding Skill Groups, and can hide away any factory construction or injection required to construct them. It also eliminates the situation where you could add the same Skill Group to multiple programmes:
SkillGroup subGroup = new SkillGroup("First Aid", programmeA);programmeA.Add(subGroup);programmeB.Add(subGroup); // what happens now?