В чем заключается суть архитектурного паттерна Event Sourcing

Ответ

Event Sourcing (источники событий) — это архитектурный паттерн, при котором состояние системы реконструируется путем последовательного применения всех произошедших событий. Вместо хранения текущего состояния (state), система хранит полную историю изменений в виде неизменяемых событий.

Ключевые принципы:

  • Все изменения — это события. Любая операция, изменяющая состояние (например, UserRegistered, OrderPlaced), сохраняется как отдельное событие в хранилище.
  • События неизменяемы (immutable). Однажды записанное событие никогда не изменяется и не удаляется. Для отмены используется компенсирующее событие (например, OrderCancelled).
  • Состояние — это проекция событий. Текущее состояние объекта (например, баланс счета) получается путем "проигрывания" всех его событий с самого начала.

Упрощенный пример на Python:

# События
class AccountOpened:
    def __init__(self, account_id):
        self.account_id = account_id

class MoneyDeposited:
    def __init__(self, amount):
        self.amount = amount

class MoneyWithdrawn:
    def __init__(self, amount):
        self.amount = amount

# Агрегат (сущность)
class BankAccount:
    def __init__(self):
        self.id = None
        self.balance = 0

    def apply(self, event):
        """Применяет событие для изменения состояния"""
        if isinstance(event, AccountOpened):
            self.id = event.account_id
        elif isinstance(event, MoneyDeposited):
            self.balance += event.amount
        elif isinstance(event, MoneyWithdrawn):
            self.balance -= event.amount

    @staticmethod
    def replay(events):
        """Восстанавливает состояние из списка событий"""
        account = BankAccount()
        for event in events:
            account.apply(event)
        return account

# Использование
event_stream = [
    AccountOpened(account_id='acc-123'),
    MoneyDeposited(amount=100),
    MoneyWithdrawn(amount=30)
]

# Восстанавливаем текущее состояние
current_account_state = BankAccount.replay(event_stream)
print(f"Account ID: {current_account_state.id}, Balance: {current_account_state.balance}")
# -> Account ID: acc-123, Balance: 70

Преимущества:

  • Полный аудит: Легко отследить, как система пришла в текущее состояние.
  • Отладка и "путешествия во времени": Возможность восстановить состояние на любой момент времени.
  • Гибкость: Можно создавать новые проекции данных (read models) из существующего потока событий, не меняя основную логику.

Недостатки:

  • Сложность реализации: Требует иного подхода к проектированию по сравнению с традиционным CRUD.
  • Согласованность в конечном счете (Eventual Consistency): Часто используется с CQRS, что приводит к задержкам в обновлении моделей для чтения.