Chapter 6 - The aggregate could cause unnecessary complexity

continues from Chapter 5 - Event sourced aggregates are hard to refactor


Let's add a little more spice to our example, introducing rule number 4:

  1. A course cannot accept more than N students
  2. N, the Course Capacity, can change at any time to any positive integer different from the current one (even if the number of currently subscribed students is larger than the new value)
  3. The course title can change at any time to any title different from the current one
  4. The student cannot join more than 10 courses

Back to the board! We need a new event for the creation of the student, and the relative command.

But here, things get complicated, since we need a new event for the subscription and a new event for the unsubscription. Both triggered by their respective commands.

Déjà-vu? Yes, because we have already met very similar events and commands when we were talking about the course. 

But now we need two more pairs, because the newly introduced rule is relative to a new aggregate: the student.

Why do we need two separate events that represent the same event in real life?
The reason is that they belong to two different aggregates.

Here is our new Student aggregate, capable of handling some commands and emitting some events.

Also the Student aggregate, similarly to the Course aggregate, will contain the subscriptions list, necessary to make decisions regarding new subscription or unsubscription requests.

The introduction of this new business rule introduces the need for some form of synchronization. 
The request to subscribe a student to a course will be handled by a component able to deliver the respective commands to both the Student and the Course. The Student and the Course then publish their respective events.

Very simple, unless one of the two aggregates refuses the command. For example, when the course is fully booked. If this case, the component that orchestrates the subscription request must react accordingly by sending a second command to the Student, to request the cancellation of the previous subscription.

Here you are, the fifth and final villain in our story.

Transactions involving more than one aggregate can lead to excessive complexity.

The main aspect underlying the idea of the aggregate is the concept of boundary.
The boundary of consistency. The boundary of the complexity.

One of the purposes of the aggregate is precisely to contain the complexity, defining small bubbles isolated from each other, to have a minimal context where it is easy to make decisions.

This concept is perfectly in line with the best practices of decoupling and high cohesion. The intention is good, but it implies greater complexity whenever a constraint spans more than one aggregate.

"It is just a long transaction!" - you may object. Long transactions are very common when the business rule spans bounded contexts. But when the rule spans multiple aggregates in the same bounded context, Is this a necessary complexity, or can it be avoided?

Here they are, all the negative elements of the aggregate:
  • Does not fit the storytelling
  • Puts focus on the model instead of behavior
  • Mixes technical and business aspects
  • Can cause unnecessary contention
  • Can cause unnecessary complexity
  • Hard to refactor

 It remains only to shoot it down.

The story continues to the next chapter
Chapter 7 -  Focus on the behavior

... or watch the full story on youtube


Popular posts from this blog

Chapter 1 - I am here to kill the aggregate

Chapter 2 - The Aggregate does not fit the storytelling

Chapter 3 - The aggregate mixes technical and business aspects