В чем заключается суть архитектурного паттерна 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, что приводит к задержкам в обновлении моделей для чтения.

Ответ 18+ 🔞

Да ты послушай, что эти умники придумали! Event Sourcing, блядь, источники событий. Это ж надо было так мозги закрутить, ёпта! Вместо того чтобы просто хранить, сколько у тебя на счету денег, они предлагают записывать КАЖДУЮ, сука, операцию, как в бухгалтерской книге. Каждое "пополнил", "снял", "открыл счет" — всё это отдельные, блядь, неизменяемые события. Как каменные скрижали, нахуй!

Представь, ты не помнишь, сколько у тебя денег, но у тебя есть полная история твоих трат и пополнений. Чтобы узнать баланс, тебе надо, блядь, взять калькулятор и просуммировать всю эту хуйню с самого начала. Вот это и есть Event Sourcing, ёбана!

Соль, блядь, в чём:

  • Всякая хрень — это событие. Зарегистрировался юзер? Событие UserRegistered. Купил что-то? Событие OrderPlaced. Всё летит в хранилище, как есть.
  • События — это святое. Записал — и нихуя не трогаешь. Хочешь отменить? Ну так запиши новое событие, компенсирующее, типа OrderCancelled. Нельзя просто взять и стереть, это ж не школьный дневник, блядь!
  • Состояние — это иллюзия, проекция! Твой текущий баланс — это просто результат проигрывания ВСЕХ событий по твоему счету, от первого до последнего. Как кинолента, ёпта!

Смотри, как это выглядит в коде, чтоб не быть голословным:

# События
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

Видишь? Мы не храним цифру 70. Мы храним три события: открыли, положили 100, сняли 30. А 70 — это просто результат их применения. Красота, блядь!

Чем это, сука, хорошо:

  • Аудит полный, как в НКВД. Всегда можно докопаться, кто, что и когда сделал. Идеально для параноиков.
  • Отладка и "путешествия во времени". Хочешь узнать, какой был баланс в прошлый вторник? Пожалуйста, проиграй события до этого момента. Машина времени, ёпта!
  • Гибкость овердохуищная. Из одного потока событий можно наделать кучу разных "представлений" данных, не трогая основную логику. Как из одного куска мяса и котлету, и гуляш.

Чем это, блядь, плохо:

  • Сложность пиздец. Голову сломаешь, пока перестроишь мышление с обычного CRUD на эту философию.
  • Согласованность в конечном счёте (Eventual Consistency). Часто эту штуку используют с CQRS, и получается, что данные в "читаемых" моделях обновляются с небольшой задержкой. То есть ты положил деньги, а в выписке они появятся не сразу. Может, через секунду, а может, и через пять. Волнение ебать, особенно если ждёшь подтверждения платежа!

В общем, инструмент мощный, но не для каждой задачи. Как молоток Тора — им гвоздь в стену не забьёшь, зато можно, блядь, целый мир перестроить.