Chapter 9 - An Event is just a fact, pure


Surely you remember that, in modeling the Student aggregate and the Course aggregate, we defined some events that looked like duplicates:

They were necessary to validate the respective invariants of the course and the student during the subscription process. If we had a weird feeling during the event storming phase, the feeling doesn't improve later. Indeed, reading the content of the event store, we can notice that for each subscription, there are two close events with the exact same content!

They differ only because they originate from one or another aggregate (represented here by the colors red and green). How can we get rid of this?

In reality, the domain event is only one: its duplication is functional to the existence of the two aggregates. 

But if we get rid of the aggregate, then as a direct consequence, we might also get rid of the duplication of these events. Indeed, if we get rid of the aggregate, then an event no longer belongs to an aggregate.
An event is just a fact, a pure fact.

We can replace the belonging of an event to an aggregate with a tagging operation. A sort of external labeling which is no longer part of the very essence of the event. These two events, purged of belonging to the aggregate, are now truly identical. It no longer makes sense to keep them separate.

We can replace them with a single business event, that has two tags. 

This grants us another exceptional advantage. Let's find out what advantage by analyzing how the system now behaves in managing a command for subscribing a student to a course.

The command handler knows what data it needs to make a decision.
It knows the invariants to verify in order to publish the StudentSubscribedToCourse Event. 
Now it can take advantage of the flexibility of a query to retrieve both student and course data in a single stream.

The command handler is able to reconstruct the model necessary to evaluate, in a single place, that the subscription does not violate the maximum number of students per course, nor the maximum number of courses per student.
If both rules are validated, it will be possible to publish a single event related to both business elements, the course and the student.

Therefore, the event store should not only be able to append new events but new DomainEvents.

append(DomainEvent[] events)

append(DomainEvent[] events, StreamQuery query, EventIdentifier lastEvent)

A Domain Event is the pure event together with the identifiers of the specific instances of domain concepts it is bonded to. In my example, it corresponds to the pair formed by the pure event and the collection of Domain Identifiers matched to it.

The Domain Identifier is an identifier, unique inside the bounded context, for a specific instance of a certain concept in my domain. In my example,  the Domain Identifier is expressed by a key-value pair where the key represents the domain concept and the value represents the instance identifier.

The domain identifiers are matched to a single event. Two events published in the same transaction could be associated with different domain identifiers.

For example, if after a student is subscribed, the course reaches its maximum capacity, I may want to publish the CourseFullyBooked Event in the same transaction. The StudentSubscribedToCourse Event will be associated with both the student and the course, while the CourseFullyBooked Event will be associated only with the course:

According to the approach I propose, events are pure. This means we can create a mapping closer to what happens in the real world.

An event does not belong to an aggregate 
An event just describes a fact that is important for the business.
An event can be related to one or multiple domain concepts, addressed by their Domain Identifiers.

Several advantages come from this approach: since an event has no owner, the business decision can easily involve several domain concepts! The decision is taken in one place, in one moment.

When we have a decision involving more than one business concept, we don't necessarily have to rely on a complex synchronization mechanism. We can decide to make a decision through a single block, capable of retrieving the context data relating to all the concepts involved.

This means less synchronization, less complexity. But, of course, it only makes sense if we are within the same bounded context.

The last advantage that this solution offers is related to flexibility. Thanks to event sourcing, I can reconstruct every model needed to make a decision, on the fly. If I got the initial modeling wrong, or if my business changes, I can modify the query and load a different model. Even if I made a mistake associating one or more events to a Domain Identifier, It would be enough to fix the problem by adding or removing the Domain Identifiers matched to the events in the event store.

The story continues to the next chapter

... or watch the full story on youtube


  1. Hi again:) Do you have an example of such an event store, that would fulfill the requirements but also be able to store millions of events? I feel that dynamic events querying is a no-go if you want to have a performant and scalable event store. No to mention the optimistic locking functionality, which limits the choices even more.

    1. Hello Andrzej, I apologize for the delay in my response as I missed your comment earlier. I am currently collaborating with AxonIQ to implement the functionality within Axon Server. Axon Server is a comprehensive solution that serves as both a messaging platform and an event store.


Post a Comment

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