Skip to content

Building blocks of Event Sourcing

Event Sourcing is a powerful architectural pattern that revolves around capturing all changes to an application's state as a sequence of immutable events. These events are stored in an event log, and the current state of the system is derived by replaying these events.

TODO: Enchance the section

What is Aggregate ?

In the Chess match example using Event Sourcing, the aggregate would be the game state itself. An aggregate is typically a single object or entity, so in this case, it would be the Game object that contains the game state and manages the application of events to update the state accordingly. The aggregate encapsulates the business logic related to the game, making it easy to reason about the game state and handle complex behavior.

Network elements, e.g. load, bus, etc..

What is State in Event Sourcing ?

In the Chess match example using Event Sourcing, the state refers to the current state of the game at any given point in time. The state is represented by the aggregate, which is constructed by applying events to it.

Current load properties at any given poin in time.

What is Projections in Event Sourcing ?

In Event Sourcing, events represent all state changes and the system state can be reconstructed from these events. However, for some query and analysis operations, working directly on events may be insufficient in terms of efficiency and performance. This is where projection comes into play.

In the Chess match example using Event Sourcing, projections are views or queries that are derived from the event stream to provide specific information or insights about the game.

Here are some examples of projections in the Chess match example:

  1. Game Summary: A projection that calculates the winner of the game, the total number of moves made, and the time spent on the game.
{
  "winner": "white",
  "totalMoves": 40,
  "timeSpent": 60 * 60 * 2
}
  1. Move Analysis: A projection that provides detailed information about each move made in the game, such as the move's legality, the number of pieces captured, and the impact on the board position.
[
  {
    "moveId": 1,
    "move": "e2e4",
    "isLegal": true,
    "piecesCaptured": 0,
    "boardPosition": [
      ...
    ]
  },
  ...
]

What is Command in Event Sourcing ?

In the Chess match example using Event Sourcing, the command represents a request to perform an action on the game, usually initiated by a user. The command encapsulates the user's intent, validates the input, and transforms it into a sequence of events that will be applied to the game aggregate.

Here are some examples of commands in a Chess match example:

  1. MakeMove: Command to make a move on the board. The command takes the source and destination squares as input and validates whether the move is legal. If the move is legal, the command generates a sequence of events (e.g., MoveEvent, PieceCapturedEvent, CastlingEvent) and publishes them to the event store.

  2. PromotePawn: Command to promote a pawn to a queen, rook, bishop, or knight when it reaches the opposite end of the board. The command takes the pawn's position as input and validates whether the pawn is indeed a pawn and has reached the opposite end of the board. If the conditions are met, the command generates a sequence of events (e.g., PawnPromotionEvent, PieceCreatedEvent) and publishes them to the event store.

  3. Castle: Command to perform a castling move, which involves moving the king and rook under certain conditions. The command takes the king's and rook's positions as input and validates whether the castling is legal. If the castling is legal, the command generates a sequence of events (e.g., KingMoveEvent, RookMoveEvent) and publishes them to the event store.

  4. Resign: Command to indicate that a player has resigned from the game. The command generates a ResignEvent and publishes it to the event store, updating the game state to reflect the resignation.

In summary, the command in this chess example represents a request to perform an action on the game and is responsible for encapsulating the user's intent, validating the input, and generating a sequence of events that will be applied to the game aggregate.

Disconnect or set out of service of the load from network. Modify value

As we indicated before, in systems developed with Event Sourcing, the final state of entities is not recorded. Instead, events that affect the state of the entities are recorded. When the client sends a query and requests the last state of the entity, the system combines the Event information it has and provides the necessary information to the client.

Let's explain this process with an example. Let's assume that the user has information such as Name, Surname, Email, Email Approved, Password and that it fulfills the following actions respectively;

  • The user creates an account with e-mail and password information
  • Updated username and surname
  • User confirmed email account

CRUD

When we store user data with a traditional system, the content of our storage environment can be as follows.

Point in time : T1 => User created account with email and password information

id name surname email emailComfirmed password lastModificationTime creationTime
9ee4d41f-3ef9-4b59-990a-ca1cd3785805 null null denizraifdurmaz@gmail.com false AQAAAAIAAYagAAAAEGerrLvi null 2024-07-25 5:56:17 PM

Point in time : T2 => Updated name and surname

id name surname email emailComfirmed password lastModificationTime creationTime
9ee4d41f-3ef9-4b59-990a-ca1cd3785805 raif durmaz denizraifdurmaz@gmail.com false AQAAAAIAAYagAAAAEGerrLvi 2024-07-25 6:00:23 PM 2024-07-25 5:56:17 PM

Point in time : T3 => User confirmed email account

id name surname email emailComfirmed password lastModificationTime creationTime
9ee4d41f-3ef9-4b59-990a-ca1cd3785805 raif durmaz denizraifdurmaz@gmail.com true AQAAAAIAAYagAAAAEGerrLvi 2024-07-25 6:03:49 PM 2024-07-25 5:56:17 PM

Event Sourcing

In the Event Sourcing structure, the sample content that may occur if the same actions are executed can be as follows.

Point in time : T1 => User created account with email and password information

id event type key event eventTime
c8487087-36a1-43eb-a885-819e711743af user-created 1234-abcd {"e-mail": "denizraifdurmaz@gmail.com", "pasword": "AQAAAAIAAYagAAAAEGerrLvi"} 2024-07-25 5:56:17 PM

Point in time : T2 => Updated name and surname

id event type key event eventTime
c8487087-36a1-43eb-a885-819e711743af user-created 1234-abcd {"e-mail": "denizraifdurmaz@gmail.com", "pasword": "AQAAAAIAAYagAAAAEGerrLvi"} 2024-07-25 5:56:17 PM
eb26eb1b-1fd3-4653-8881-7e0cb9ddce2c parameter-change 1234-abcd {"name": "raif.", "surname": "durmaz"} 2024-07-25 6:00:23 PM

Point in time : T3 => User confirmed email account

id event type key event eventTime
c8487087-36a1-43eb-a885-819e711743af user-created 1234-abcd {"e-mail": "denizraifdurmaz@gmail.com", "pasword": "AQAAAAIAAYagAAAAEGerrLvi"} 2024-07-25 5:56:17 PM
eb26eb1b-1fd3-4653-8881-7e0cb9ddce2c parameter-change 1234-abcd {"name": "raif", "surname": "durmaz"} 2024-07-25 6:00:23 PM
b8c8be5a-4674-43d4-b824-eaffebc55533 parameter-change 1234-abcd {"emailComfirmed": "true"} 2024-07-25 6:03:49 PM

With the Event Sourcing method, it is now possible to create the state of the user entity at any time. For this, we need to obtain all Event information that has occurred before the desired time. Then we execute the data of the occurrences on an empty user object in order according to the date of occurrence.

In case of using Event Sourcing method, we need to solve the problem of;

  • re-creating the entity every time
  • filtering it according to the fields of the entity

at this is where CQRS comes into play

What is the CQRS ?

CQRS stands for Command Query Responsibility Segregation and is basically considered a responsibility segregation pattern. Differentiated from traditional architectures, CQRS aims to process transactions in different ways by separating commands (Command) and queries (Query) into separate components. Commands are used to perform data changes, while queries are used to read and present data. In this way, data reading and data writing are handled in different ways and appropriate optimizations can be made for each type of operation.

Implementing Event Sourcing

If you have look the diagram above, we have to handle process Query and Command requests in different databases. If an event is to be created, we will store it as Event Sourcing as in the 'Command Zone'. In order to store events we can use different databases like relational databases, no-sql databases or different designs.

However we would like to store events with EventStoreDB techonology.

To streamline the implementation of the Command Query Responsibility Segregation (CQRS) pattern, several frameworks and libraries can assist you in building the necessary components:

MediatR: MediatR is a popular open-source library that simplifies the implementation of the mediator pattern. It enables loose coupling between command handlers, query handlers, and domain logic.

Common Misconceptions

“My application publishes/emits/uses events, therefore I am using Event Sourcing.”

Not necessarily.

The term event is fairly common in any modern application, especially those that are distributed in nature. As you interact with your application, it can emit events as part of fulfilling an operation, but if you are persisting only the current state, it is not an Event Sourcing application.

“I can’t use Event Sourcing because it is going to be too slow.”

Not necessarily.

Like any pattern, Event Sourcing, provides a proven way to solve certain types of problems. You have to first assess if your application has the properties that can benefit from using this pattern.