Chapter 4 - The larger the Aggregate, the greater the contention

continues from Chapter 3 - The aggregate mixes technical and business aspects

......

Let's add something new to our example, introducing rule number 3:

  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

We need to introduce a new event, to represent the fact that the course has been renamed. And a new command to request the change of the Course's title.

Who should this command be sent to?
The most common answer is: "to the Course".


The Course aggregate is now responsible for something more than before. It may seem irrelevant, but it hides a pitfall.

Let me introduce you to the third villain in our story:

The larger the Aggregate, the greater the contention!


The natural attitude to choose as aggregates the business concepts that are part of our mental model can lead to a sometimes unnecessary expansion of the boundaries of the aggregate. Whenever we identify a new event pertaining to a certain mental concept, we tend to assign it to the respective aggregate.

But it is important to keep in mind that the boundary of consistency is also the boundary of concurrency. The correct sizing of the aggregate can significantly affect application performance.

If we decided - absurdly - to model our system as a single, large aggregate, this would prevent any form of concurrent access. All operations should be performed sequentially, as they relate to a single aggregate, which would act as a contention point.

Let's assume the system receives concurrently two commands related to the same course. The first command requests the capacity of the course is updated. The second command requests the course is renamed. 

Our instinct tells us immediately that these two operations will not conflict.

Both commands are handled at the same time. Two replicas of the same aggregate instance are loaded.
Each is updated based on the command being handled. The problem arises when we try to persist the transactions.

Since the aggregate guarantees internal consistency, accepting two concurrent modifications is impossible. While the first of the two transactions will be accepted, the second will necessarily be rejected.


It doesn't matter that, within the aggregate, the modified data are completely independent. It doesn't matter that the change of the course's title will never affect the change of its capacity or the opposite.
The fact that two commands are managed by the same instance of the aggregate is sufficient to prevent them from being executed simultaneously. Assigning both these commands to the same aggregate causes an increase in the contention that would not be necessary, as we know that the information they modify has no reciprocal implications. Nevertheless, they must be handled sequentially since they are part of the same aggregate.

What alternative do we have?

Instead of a single aggregate - the Course - we could design two aggregates:
  1. Course Subscriptions, responsible for capacity and subscriptions consistency,
  2. Course Info, responsible for Course details consistency.
Both solutions have pros and cons. A single aggregate is simpler, although it causes more contention.
Two smaller aggregates reduce the contention, allowing for greater concurrency at the price of increased complexity.

As always, it is a matter of identifying the best compromise.

But what does happen if we start implementing the Course aggregate and later realize that we should have implemented CourseInfo and CourseSubscriptions instead?

The story continues to the next chapter
Chapter 5 - Event Sourced aggregates are hard to refactor

... or watch the full story on youtube

Comments

Popular posts from this blog

Chapter 1 - I am here to kill the aggregate

Chapter 2 - The Aggregate does not fit the storytelling

Chapter 8 - The death of the aggregate