Ответ
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. Это когда команды (запись) и запросы (чтение) разделены, как церковь от государства. Модель для записи — она на событиях завязана, а для чтения — отдельная, оптимизированная, чтобы быстро данные отдавать. Вместе они — сила, блядь!