Event Sourcing

↑ 8.2 Архитектурные стили и DDD · ← Предыдущая · → Следующая

Обязательно~3 мин чтенияУровень: senior

Зачем это на собесе

Спрашивают, когда это оправдано и какие сложности несёт.

Объяснение

Вместо хранения текущего состояния сохраняется последовательность событий, изменивших его. Состояние — результат их проигрывания.

OrderCreated → ItemAdded → ItemAdded → OrderPaid → OrderShipped
                                  ↓ replay
                           состояние Order = Shipped, items = 2
public abstract record OrderEvent(Guid OrderId, DateTimeOffset At);
public record ItemAdded(Guid OrderId, DateTimeOffset At, Sku Sku, int Qty) : OrderEvent(OrderId, At);
public record OrderPaid(Guid OrderId, DateTimeOffset At) : OrderEvent(OrderId, At);
 
public class Order
{
    public Status Status { get; private set; }
    public static Order Rehydrate(IEnumerable<OrderEvent> history) { var o = new Order(); foreach (var e in history) o.Apply(e); return o; }
    private void Apply(OrderEvent e) { switch (e) { case OrderPaid: Status = Status.Paid; break; /* ... */ } }
}
ПлюсыМинусы
полный аудит и историясложность и порог входа
можно восстановить состояние на любой моментэволюция схемы событий (версионирование, upcasting)
естественная основа для проекций и интеграцииeventual consistency проекций
отладка «что произошло»запросы по текущему состоянию — через проекции
удаление персональных данных (GDPR): шифрование ключами, crypto-shredding

Техники: snapshots (ускорение загрузки), проекции/read-модели, оптимистичная блокировка по версии потока, хранилища: EventStoreDB, Marten (PostgreSQL), собственная таблица events.

Когда оправдан: строгий аудит (финансы), сложные доменные процессы с историей, потребность в темпоральных запросах. В остальных случаях — избыточен.

Нюансы и подводные камни

  • Изменение формата событий нельзя выполнить «миграцией на месте»: события неизменяемы.
  • «Плохое» событие не удаляют, а компенсируют новым.
  • Проекции нужно уметь перестраивать (replay).
  • Не путайте с журналом изменений (audit log) — он проще и часто достаточен.
  • Event Sourcing ≠ CQRS ≠ шина событий.

Практика

  1. Смоделируйте счёт/заказ через события и восстановите состояние.
  2. Реализуйте проекцию текущих остатков и её перестроение.
  3. Используйте Marten для потока событий в PostgreSQL.

Вопросы с ответами

Связанные темы