Senior
What is the Event Sourcing pattern?
sobes.tech AI
Answer from AI
Event Sourcing template is an approach to building applications where all changes to the system's state are stored as a sequence of immutable events. Instead of storing the current state of an object, a complete log of actions that led to this state is preserved.
Key features:
- Immutability of events: Once created, an event is never changed or deleted.
- Sequence: Events are stored in strict chronological order.
- Change archive: The full history of all changes is available for auditing, debugging, and analysis.
- State recovery: The current state of an object can be reconstructed by "replaying" all applicable events from the beginning of time or a specific point.
Advantages:
- Unlimited retrospective observation: Easily see how the system arrived at its current state.
- Audit and debugging: The complete history of events facilitates error cause analysis and action auditing.
- Parallel processing and scaling: Separating event recording from processing makes it easier to scale the application.
- Support for CQRS (Command Query Responsibility Segregation): Events are often used to update read models.
Disadvantages:
- Implementation complexity: Requires a more complex infrastructure for storing and processing events.
- Performance during replay: Restoring state from a large number of events can be slow. Optimizations like snapshots are needed.
- Event schema changes: Evolving event schemas can be a challenging task.
Example:
Imagine a bank account. Instead of updating the balance directly, we store events: "Deposit X", "Withdrawal Y". The current balance is calculated by summing all operations.
// Example event class
public class MoneyDepositedEvent {
private final String accountId;
private final BigDecimal amount;
private final LocalDateTime timestamp;
public MoneyDepositedEvent(String accountId, BigDecimal amount, LocalDateTime timestamp) {
this.accountId = accountId;
this.amount = amount;
this.timestamp = timestamp;
}
// getters
}
// Example aggregate (bank account) applying events
public class BankAccount {
private String id;
private BigDecimal balance;
private List<Object> changes = new ArrayList<>(); // list of unapplied events
public BankAccount(String id) {
this.id = id;
this.balance = BigDecimal.ZERO;
}
// Method generating an event
public void deposit(BigDecimal amount) {
if (amount.compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("Amount must be positive");
}
MoneyDepositedEvent event = new MoneyDepositedEvent(this.id, amount, LocalDateTime.now());
apply(event); // Apply event to current state
changes.add(event); // Add to changes list for saving
}
// Method applying an event to the aggregate's state
private void apply(MoneyDepositedEvent event) {
this.balance = this.balance.add(event.getAmount());
}
// Method to restore state from event history
public static BankAccount loadFromHistory(String id, List<Object> history) {
BankAccount account = new BankAccount(id);
for (Object event : history) {
if (event instanceof MoneyDepositedEvent) {
account.apply((MoneyDepositedEvent) event);
}
// Handling other event types...
}
return account;
}
// getters and other methods
}