Event sourcing: Part 2 — Example and Architecture

Abhinav Vinci
4 min readMar 7, 2023

--

How Event Sourcing Works? #Internal Architecture

https://www.upsolver.com/blog/cqrs-event-sourcing-build-database-architecture

Event sourcing is a way of storing data as a sequence of events, rather than storing the current state of the system.

The internal architecture typically includes:

Event Store

The event store is a persistent storage system that stores a stream of events. Each event represents a change to the system’s state, and includes information such as the type of event, the time of the event, and any relevant data. The event store can be implemented using relational databases, NoSQL databases, or specialized event stores.

Event Stream:

  • An event stream is a sequence of events that represent the history of changes to the system’s state.
  • Events in the event stream are immutable, meaning they cannot be changed. This is important because it ensures that the history of changes to the system’s state is preserved and cannot be altered.
  • Instead of modifying existing events, any changes to the system’s state are recorded as new events that are added to the end of the event stream.
https://stackoverflow.com/questions/56728979/event-sourcing-why-a-dedicated-event-store

Command Processor:

The command processor receives requests to modify the system’s state, called commands, and applies them to the current state to produce a new state. After the new state is produced, the command processor generates a new event and adds it to the event stream.

Query Processor:

The query processor reads data from the event stream and generates a current view of the system’s state. This view can be used to display data to users or provide data to other systems.

Materialized View:

It is a read-only view of the event stream that is optimized for a specific use case, such as generating reports or feeding data to other systems. Projections are generated by reading data from the event stream and transforming it into a format that is suitable for the use case.

What does the Event Sourcing Code look like?

Below is a simplified example, but it demonstrates the basic idea of event sourcing: storing a stream of events that represent changes to the system’s state, rather than storing the current state itself. By doing so, we can easily track changes to the system over time and perform auditing or other analysis tasks.

class BankAccount:
def __init__(self, account_id: int) -> None:
self.account_id = account_id
self.balance = 0
self.transaction_history = []

def deposit(self, amount: float) -> None:
self.balance += amount
self.transaction_history.append({"type": "deposit", "amount": amount})

def withdraw(self, amount: float) -> None:
if self.balance < amount:
raise ValueError("Insufficient funds")
self.balance -= amount
self.transaction_history.append({"type": "withdraw", "amount": amount})

def get_balance(self) -> float:
return self.balance

def get_transaction_history(self) -> List[Dict]:
return self.transaction_history
  • Instead of storing the current balance directly, we store a list of transactions that represent changes to the account balance over time.
  • When a deposit or withdrawal is made, we update the account balance and append a new transaction to the transaction history.

What are the alternatives to Event Sourcing ?

Event sourcing may not always be the best choice. Some alternatives to event sourcing are:

  1. CRUD-based systems: CRUD (Create, Read, Update, Delete) systems are the most common approach to managing data. In these systems, data is stored as a set of records, and each record represents the current state of an entity. While CRUD systems lack the advantages of event sourcing, they can be easier to develop and maintain.
  2. Snapshot-based systems: Snapshot-based systems store the current state of an entity, along with a set of snapshots that represent previous states. This approach can provide some of the benefits of event sourcing, such as easy auditing and the ability to revert to previous states, while also being more efficient than storing a full history of events.
  3. CQRS: Command Query Responsibility Segregation (CQRS) is another design pattern that separates the responsibility of reading data from that of writing data. In a CQRS system, commands are used to modify state, while queries are used to read state. While this pattern does not necessarily involve storing events, it can provide some of the same benefits as event sourcing, such as easier auditing and scalability.
  4. Hybrid systems: Hybrid systems combine elements of event sourcing with other patterns, such as CRUD or CQRS. For example, a system might use event sourcing to store certain types of data, while using a more traditional database to store other types of data.

Event Sourcing Concepts:

Aggregate: An aggregate is a collection of related events that represent a single transaction or operation on the system’s state. Aggregates are used to ensure consistency and provide a clear boundary between different parts of the system.

  • An aggregate is defined by a set of rules, called the aggregate’s invariant, that ensure the integrity of the aggregate’s state. The invariant specifies the conditions that must be met for the aggregate’s state to be considered valid.
  • For example, an invariant for a customer aggregate might specify that a customer must have a unique email address and a valid phone number.
  • When a command is received by the system to modify the state of an aggregate, the command is processed by the aggregate’s command handler.

--

--