Потоковая обработка
Event Sourcing
Клиент звонит: «Почему мой заказ отменён?» В классической БД - загадка: поле status='cancelled', кто и когда его выставил - неизвестно. В Event Sourcing - полная история: OrderPlaced → PaymentFailed (3 попытки) → OrderCancelled. Audit log бесплатно, time travel бесплатно, rebuild любой проекции - рутина.
- **LinkedIn** использует Event Sourcing для системы уведомлений: возможность перестроить read model без потери истории критична при изменении алгоритмов
- **Axon Framework** (Java) и **EventStoreDB** - продакшн-инструменты для Event Sourcing, используемые в банках и страховых компаниях
- **Git** - пример Event Sourcing: commits = события, рабочий каталог = проекция, checkout = rebuild на определённую версию
Event Store: структура и append-only
Обычная база данных хранит текущее состояние: запись обновляется, предыдущее значение теряется. **Event Sourcing** переворачивает это: вместо текущего состояния хранится **полная история изменений** - последовательность событий. Текущее состояние вычисляется из событий на лету.
**Event Store** - это append-only лог: события только добавляются, никогда не изменяются и не удаляются. Каждое событие - факт, который уже произошёл: `OrderPlaced`, `ItemAdded`, `OrderShipped`. Не «установить status='shipped'», а «произошло событие OrderShipped в момент T».
**Superpowers Event Sourcing:** полный audit log бесплатно, возможность «путешествия во времени» (состояние на любой момент прошлого), debug через replay событий, возможность добавить новые проекции ретроспективно.
В Event Sourcing при обновлении заказа (смена адреса доставки) в базе происходит:
Projections: материализованные представления
События хранят историю, но читать состояние из сырых событий каждый раз - медленно. **Проекция** - это материализованное представление, вычисленное из событий и оптимизированное для чтения. Проекция подписывается на события и обновляется по мере их появления.
Одни и те же события могут порождать **несколько проекций** для разных целей: одна проекция для UI (текущий статус заказа), другая для аналитики (Revenue по регионам), третья для уведомлений. Добавление новой проекции не требует изменений в event store - достаточно построить её из истории событий.
**CQRS + Event Sourcing:** Command side пишет события, Query side читает проекции. Команды и запросы используют разные модели данных - это ключевой паттерн для масштабируемых систем с Event Sourcing.
Нужно добавить новую проекцию: аналитику по времени обработки заказов. В Event Sourcing это требует:
Snapshots для оптимизации
Агрегат с долгой историей может иметь тысячи событий. Каждый раз проигрывать их все для получения текущего состояния - дорого. **Снапшот** (snapshot) - это сохранённое состояние агрегата на определённой версии событий. При загрузке достаточно взять последний снапшот и применить только события после него.
**Когда нужен снапшот:** агрегат с историей более 50-100 событий, высокая частота загрузки. Снапшот - оптимизация производительности, не архитектурное требование. Начинайте без снапшотов, добавляйте когда измерения покажут проблему.
Агрегат имеет снапшот на версии 100 и 30 новых событий (версии 101-130). Сколько событий нужно применить при загрузке?
Rebuilding состояния из событий
Проекция устарела. Завели новое поле в событии, изменили бизнес-логику, обнаружили баг в reducer. Классическая БД требует сложной миграции. Event Sourcing даёт суперспособность: **просто выбросить проекцию и построить заново** из неизменных событий.
**Rebuild (replay)** - это применение всей истории событий к новому или изменённому reducer. При правильной архитектуре rebuild - рутинная операция, а не аварийная ситуация. Системы с Event Sourcing проектируют с расчётом на регулярный rebuild проекций.
**Производительность rebuild:** миллион событий - это минуты, не дни. Event store читается последовательно (оптимально для дисков), batch-обработка распараллеливается по партициям. Kafka как event store позволяет параллельный consumer group rebuild.
Event Sourcing сложен и подходит только для больших систем с большой командой.
Event Sourcing добавляет сложность проекций и снапшотов, но даёт бесплатный audit log, time travel и гибкий rebuild. Оправдан в доменах с высокой ценностью истории: финансы, инвентарь, workflow.
Сложность управляема при правильных инструментах (Kafka, EventStoreDB). Главный вопрос - нужна ли история изменений как первоклассная концепция в вашем домене.
Обнаружена ошибка в логике reducer проекции (неверно считается сумма). Действие в Event Sourcing:
Event Sourcing
- Event Store: append-only лог событий-фактов; состояние вычисляется из событий, никогда не хранится напрямую
- Проекции: reducer применяет события к состоянию; одни события порождают множество проекций для разных нужд
- Снапшоты: оптимизация для агрегатов с долгой историей - снапшот + события после него
- Rebuild: исправить reducer + перестроить проекцию из истории - killer feature Event Sourcing
Связанные темы
Event Sourcing соединяется с несколькими архитектурными паттернами:
- Exactly-Once Semantics — Idempotent projections - естественная защита от дублей при replay
- CQRS — Command side пишет события, Query side читает проекции
- Kafka: основы — Kafka как event store: retention, consumer groups для rebuild
Вопросы для размышления
- В каких доменах история изменений ценна настолько, чтобы оправдать сложность Event Sourcing?
- Как обрабатывать изменение схемы событий (добавление полей) без миграции всего event store?
- Чем Event Sourcing отличается от CDC (Change Data Capture) - и когда один паттерн предпочтительнее другого?