Event Sourcing Pattern (original) (raw)

Last Updated : 13 May, 2026

Event Sourcing records every change as an event instead of just storing the current state, creating a complete history of data changes. The current state can be rebuilt anytime by replaying these events, helping track how data evolved over time.

**Example: In a banking system, instead of storing only the final account balance, every transaction like “Money Deposited”, “Money Withdrawn”, and “Transfer Completed” is stored as an event. The current balance is calculated by replaying all these events in order.

devops_17

Event Sourcing

Real World Applications

Event Sourcing finds applications in diverse domains and use cases where tracking and analyzing historical data is crucial. Below are some notable examples:

Components

Event Sourcing in system design revolves around several core concepts and components:

Working

The steps to understand how event sourcing pattern works are:

Implementation Example

Understand event sourcing pattern with the help of an example of registration system:

1. Registration System Before Event Sourcing

In a traditional event registration system, updates are made directly to the database, changing the current state immediately. This makes it hard to track history, maintain past data, and manage notifications for registration changes.

traditional registration class in which there are several issues:

#include #include

enum class RegistrationState { REGISTERED, CANCELLED };

class Database { public: static void update(Registration* registration) { // Update the database directly } };

class Registration { private: RegistrationState state; std::string userId; std::string eventId;

public: void registerUser(const std::string& userId, const std::string& eventId) { this->state = RegistrationState::REGISTERED; this->userId = userId; this->eventId = eventId; // Update the database directly Database::update(this); }

void cancel() {
    this->state = RegistrationState::CANCELLED;
    // Update the database directly
    Database::update(this);
}

};

Java

public class Registration { private RegistrationState state; private String userId; private String eventId;

public void register(String userId, String eventId) {
    this.state = RegistrationState.REGISTERED;
    this.userId = userId;
    this.eventId = eventId;
    // Update the database directly
    Database.update(this);
}

public void cancel() {
    this.state = RegistrationState.CANCELLED;
    // Update the database directly
    Database.update(this);
}

}

Python

class RegistrationState: REGISTERED = 'REGISTERED' CANCELLED = 'CANCELLED'

class Database: @staticmethod def update(registration): # Update the database directly pass

class Registration: def init(self): self.state = None self.userId = None self.eventId = None

def register(self, userId, eventId):
    self.state = RegistrationState.REGISTERED
    self.userId = userId
    self.eventId = eventId
    # Update the database directly
    Database.update(self)

def cancel(self):
    self.state = RegistrationState.CANCELLED
    # Update the database directly
    Database.update(self)

JavaScript

class Database { static update(registration) { // Update the database directly } }

class Registration { constructor() { this.state = null; this.userId = null; this.eventId = null; }

register(userId, eventId) {
    this.state = RegistrationState.REGISTERED;
    this.userId = userId;
    this.eventId = eventId;
    // Update the database directly
    Database.update(this);
}

cancel() {
    this.state = RegistrationState.CANCELLED;
    // Update the database directly
    Database.update(this);
}

}

const RegistrationState = { REGISTERED: 'REGISTERED', CANCELLED: 'CANCELLED' };

`

2. Registration System Using Event Sourcing

With the move to an event-sourced architecture, the system now records each significant change as an event, allowing better tracking and historical analysis.

event sourced registration class:

C++ `

#include #include

class Event {}; class EventRegisteredEvent : public Event { public: std::string userId; std::string eventId; EventRegisteredEvent(std::string userId, std::string eventId) : userId(userId), eventId(eventId) {} }; class RegistrationCancelledEvent : public Event {};

enum class RegistrationState { REGISTERED, CANCELLED };

class RegisterForEventCommand { public: std::string getUserId() { return userId; } std::string getEventId() { return eventId; } private: std::string userId; std::string eventId; }; class CancelRegistrationCommand {};

class Registration { private: RegistrationState state; std::string userId; std::string eventId;

public: RegistrationState getState() { return state; }

std::vector<Event> process(RegisterForEventCommand cmd) {
    return { EventRegisteredEvent(cmd.getUserId(), cmd.getEventId()) };
}

std::vector<Event> process(CancelRegistrationCommand cmd) {
    return { RegistrationCancelledEvent() };
}

void apply(EventRegisteredEvent event) {
    this->state = RegistrationState::REGISTERED;
    this->userId = event.userId;
    this->eventId = event.eventId;
}

void apply(RegistrationCancelledEvent event) {
    this->state = RegistrationState::CANCELLED;
}

};

Java

public class Registration extends ReflectiveMutableCommandProcessingAggregate<Registration, RegistrationCommand> { private RegistrationState state; private String userId; private String eventId;

public RegistrationState getState() {
    return state;
}

public List<Event> process(RegisterForEventCommand cmd) {
    return EventUtil.events(new EventRegisteredEvent(cmd.getUserId(), cmd.getEventId()));
}

public List<Event> process(CancelRegistrationCommand cmd) {
    return EventUtil.events(new RegistrationCancelledEvent(userId, eventId));
}

public void apply(EventRegisteredEvent event) {
    this.state = RegistrationState.REGISTERED;
    this.userId = event.getUserId();
    this.eventId = event.getEventId();
}

public void apply(RegistrationCancelledEvent event) {
    this.state = RegistrationState.CANCELLED;
}

}

Python

from typing import List

class Event: pass class EventRegisteredEvent(Event): def init(self, user_id, event_id): self.user_id = user_id self.event_id = event_id class RegistrationCancelledEvent(Event): pass

class RegistrationState: REGISTERED = 'REGISTERED' CANCELLED = 'CANCELLED'

class RegisterForEventCommand: def init(self, user_id, event_id): self.user_id = user_id self.event_id = event_id def get_user_id(self): return self.user_id def get_event_id(self): return self.event_id class CancelRegistrationCommand: pass

class Registration: def init(self): self.state = None self.user_id = None self.event_id = None

def get_state(self):
    return self.state

def process(self, cmd):
    if isinstance(cmd, RegisterForEventCommand):
        return [EventRegisteredEvent(cmd.get_user_id(), cmd.get_event_id())]\n        elif isinstance(cmd, CancelRegistrationCommand):
        return [RegistrationCancelledEvent()]

def apply(self, event):
    if isinstance(event, EventRegisteredEvent):
        self.state = RegistrationState.REGISTERED
        self.user_id = event.user_id
        self.event_id = event.event_id
    elif isinstance(event, RegistrationCancelledEvent):
        self.state = RegistrationState.CANCELLED

JavaScript

class Event {} class EventRegisteredEvent extends Event { constructor(userId, eventId) { super(); this.userId = userId; this.eventId = eventId; } } class RegistrationCancelledEvent extends Event {}

class RegistrationState { static REGISTERED = 'REGISTERED'; static CANCELLED = 'CANCELLED'; }

class RegisterForEventCommand { constructor(userId, eventId) { this.userId = userId; this.eventId = eventId; } getUserId() { return this.userId; } getEventId() { return this.eventId; } } class CancelRegistrationCommand {}

class Registration { constructor() { this.state = null; this.userId = null; this.eventId = null; } getState() { return this.state; } process(cmd) { if (cmd instanceof RegisterForEventCommand) { return [new EventRegisteredEvent(cmd.getUserId(), cmd.getEventId())]; } else if (cmd instanceof CancelRegistrationCommand) { return [new RegistrationCancelledEvent()]; } } apply(event) { if (event instanceof EventRegisteredEvent) { this.state = RegistrationState.REGISTERED; this.userId = event.userId; this.eventId = event.eventId; } else if (event instanceof RegistrationCancelledEvent) { this.state = RegistrationState.CANCELLED; } } }

`

By transitioning to an event-sourced architecture, the event registration system gains a clear advantage in tracking registration states, maintaining a complete history of actions, and ensuring timely notifications to users.

Challenges

While Event Sourcing offers various benefits, it also presents several challenges:

When to Use Event Sourcing Pattern

Use Event Sourcing Pattern when:

When Not to Use Event Sourcing Pattern

Event Sourcing may not be the best choice in these situations: