Life inside an Aggregate Root, part 2

This is the second half of a two-part article. Read the first half here: Life inside an Aggregate Root, part 1.

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.

  1. 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.
  2. Anemic domain model: Training Programmes and Skill Groups are just dumb containers here. The actual logic is being performed in the application services layer.
  3. 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?

Much better!

October 14, 2009

5 Comments

Chris on June 7, 2011 at 4:19 pm.

Hi, thanks for the efforts for writing these articles! They help alot.

Anways, I ve got some comments (even though this post is kinda old):

SkillGroup subGroup = new SkillGroup(“First Aid”, programmeA);
programmeA.Add(subGroup);
programmeB.Add(subGroup); // what happens now?

I case of “what happens now?”… Inside the programme.Add-Method, couldn”t there just be the appropriate business logic? Like:
public void Add(SkillGroup group)
{
if(group.TrainingProgramme != null)
throw new DomainExpception(…);
}

From my point of view, one good aspect of OOD is to pass objects around. This allows, in case of refactoring, to reduce the number of contracts to be changed.

In your example, if SkillGroup would have more than one property, like 10 or 20, all these need to be passed via the Add-Method, right? So if SkillGroup gets a new property later on, I must change the SkillGroup constructor, probably a factory, and the Add-method to support the new property. Do you have any experience regarding refactoring in this case?

Thanks in advance

Chris

Richard on June 8, 2011 at 9:54 am.

@ Chris: you could pass in a Parameter Object. However it happens, the point was that the aggregate root is responsible for creating child objects (they don’t just appear out of thin air and get passed to it).

ignace on June 29, 2013 at 6:03 pm.

How would you then add Skills?

trainingProgramme.addSkill() ?

OR

trainingProgramme.getTopicById(topicId).addSkill() ?

ouaee on October 11, 2013 at 10:54 pm.

Do you think to continue Life inside an Aggregate Root series?

Leave a Reply