Что такое Event Sourcing (Событийное снабжение)?

Ответ

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

Журнал событий становится единственным источником правды (Single Source of Truth).

Основная идея: Вместо того чтобы выполнять UPDATE User SET status = 'active' WHERE id = 123, мы сохраняем событие UserActivated { UserID: 123, Timestamp: ... }.

Пример на Go:

// События
type AccountCreated struct {
    ID      string
    Owner   string
}
type MoneyDeposited struct {
    Amount int
}
type MoneyWithdrawn struct {
    Amount int
}

// Агрегат (сущность)
type Account struct {
    ID      string
    Owner   string
    Balance int
}

// Восстановление состояния из истории событий
func NewAccountFromEvents(events []interface{}) *Account {
    a := &Account{}
    for _, event := range events {
        a.Apply(event)
    }
    return a
}

// Применение одного события к состоянию
func (a *Account) Apply(event interface{}) {
    switch e := event.(type) {
    case AccountCreated:
        a.ID = e.ID
        a.Owner = e.Owner
    case MoneyDeposited:
        a.Balance += e.Amount
    case MoneyWithdrawn:
        a.Balance -= e.Amount
    }
}

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

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

Недостатки:

  • Сложность реализации: Паттерн сложнее для понимания и реализации, чем традиционное CRUD-хранение.
  • Версионирование событий: Если структура события меняется, нужно поддерживать обратную совместимость для корректного воспроизведения старых событий.
  • Производительность восстановления: Восстановление состояния для сущностей с очень длинной историей может быть медленным (решается с помощью снимков состояния — snapshots).

Связь с CQRS: Event Sourcing часто используется вместе с паттерном CQRS (Command Query Responsibility Segregation), где модели для записи (основанные на событиях) отделены от моделей для чтения (оптимизированных для запросов).

Ответ 18+ 🔞

А, слушай, смотри, вот эта штука — Event Sourcing. Ну, архитектурный такой паттерн, блядь. Суть в чём, а? В том, что все изменения состояния твоего приложения не просто так в базу пишутся, а сохраняются как последовательность неизменяемых событий. Это как кино на плёнке, ёпта! Текущее состояние системы не лежит готовеньким, нет! Оно высчитывается, блядь, как будто ты с самого начала прокручиваешь все эти события одно за другим.

И этот журнал событий становится, понимаешь, единственным источником правды, священным писанием, Single Source of Truth, мать его.

Основная мысль, если по-простому: Вместо того чтобы тупо делать UPDATE User SET status = 'active' WHERE id = 123, мы, как культурные люди, сохраняем событие UserActivated { UserID: 123, Timestamp: ... }. Запомнил? Как будто запись в историческую книгу: "В такой-то день, Иван Иваныч активировался".

Вот, смотри, пример на Go, не трогай его, он святой:

// События
type AccountCreated struct {
    ID      string
    Owner   string
}
type MoneyDeposited struct {
    Amount int
}
type MoneyWithdrawn struct {
    Amount int
}

// Агрегат (сущность)
type Account struct {
    ID      string
    Owner   string
    Balance int
}

// Восстановление состояния из истории событий
func NewAccountFromEvents(events []interface{}) *Account {
    a := &Account{}
    for _, event := range events {
        a.Apply(event)
    }
    return a
}

// Применение одного события к состоянию
func (a *Account) Apply(event interface{}) {
    switch e := event.(type) {
    case AccountCreated:
        a.ID = e.ID
        a.Owner = e.Owner
    case MoneyDeposited:
        a.Balance += e.Amount
    case MoneyWithdrawn:
        a.Balance -= e.Amount
    }
}

И чем это, спрашивается, хорошо? Преимущества, блядь:

  • Полный аудиторский след, ёпта! Всегда можно докопаться: кто, что и когда натворил. Вся история, как на ладони. Скрыться не получится, хитрая жопа!
  • Отладка и "путешествия во времени": Охуенно же! Можно состояние системы на любой день рождения восстановить. Баг проявился? Отматывай плёнку назад и смотри, где начало этого пиздеца.
  • Аналитика: Из одного и того же журнала событий можно наделать кучу разных "проекций" для чтения. Как из одного куска мяса — котлеты, гуляш и строганина. Удобно, блядь!

Но не всё так гладко, конечно. Недостатки, мать их:

  • Сложность реализации, ёперный театр! Паттерн не для слабаков. Гораздо сложнее, чем тупое CRUD-хранилище. Мозги надо включать.
  • Версионирование событий — вот это пиздец! Если структуру события поменял, то надо думать, как старые события читать. Обратная совместимость, блядь, головная боль.
  • Производительность восстановления: Представь, у тебя сущность, как Герасим, — история на десять тысяч событий. Чтобы её состояние получить, надо все десять тысяч применить. Медленно, сука! Но умные люди придумали "снимки" (snapshots) — типа сохранённые точки, от которых можно оттолкнуться.

И ещё важный момент — связь с CQRS. Event Sourcing часто ходит парой с паттерном CQRS. Это когда команды (запись) и запросы (чтение) разделены, как церковь от государства. Модель для записи — она на событиях завязана, а для чтения — отдельная, оптимизированная, чтобы быстро данные отдавать. Вместе они — сила, блядь!