System Design Space
Граф знанийНастройки

Обновлено: 24 марта 2026 г. в 17:36

Event-Driven Architecture: Event Sourcing, CQRS, Saga

medium

Практический разбор event-driven подхода: как проектировать потоки событий, когда применять Event Sourcing и CQRS, и как реализовывать Saga для распределенных транзакций.

Событийная архитектура ценна не лозунгом про loose coupling, а тем, как она перераспределяет ответственность во времени, между сервисами и между моделями данных.

Глава помогает разложить Event Sourcing, CQRS и Saga на отдельные инженерные решения, чтобы было видно, где асинхронность действительно упрощает систему, а где она приносит replay complexity, schema evolution, eventual consistency lag и более дорогую observability.

В инженерных обсуждениях это позволяет спокойно разбирать choreography vs orchestration, auditability, business-process correctness и риск зависающих workflow без сваливания всего в универсальное "добавим брокер и станет гибче".

Практическая польза главы

Границы событий

Проектируйте доменные события с устойчивой схемой и версионированием, чтобы evolution не ломала потребителей.

Согласование потоков

Разделяйте orchestration/choreography осознанно и фиксируйте, где нужна central coordination.

Replay и recovery

Закладывайте replay-стратегию, DLQ и observability event-path, чтобы отказы были восстанавливаемыми.

Interview storytelling

Аргументируйте, когда EDA действительно снижает связность, а когда добавляет неоправданную операционную сложность.

Reference

Martin Fowler: Event Sourcing

Классическое объяснение идеи event sourcing и причин применения.

Открыть материал

Event-Driven Architecture (EDA) переносит фокус с синхронных вызовов на поток доменных событий. Это дает гибкость и масштабируемость, но требует дисциплины в контракте событий, идемпотентности и наблюдаемости. Практически EDA часто строится вокруг трех паттернов: Event Sourcing, CQRS и Saga.

Event-Driven фундамент

Событие - факт

События описывают то, что уже произошло в домене, и должны быть неизменяемыми.

Асинхронность по умолчанию

Производитель и потребители слабо связаны, обмениваются через broker/log.

Eventual consistency

Между write и read-моделью появляется лаг, который нужно учитывать в UX и API.

Анимированные потоки данных

Ниже визуализация потоков для трех ключевых сценариев: базовый event pipeline, разделение read/write в CQRS и координация шагов в Saga.

Data Flow Animations

Каждая анимация запускается отдельно по кнопке `Старт`.

Event-Driven Pipeline

1

Command API

Получает бизнес intent

2

Domain Aggregate

Валидирует и принимает решение

3

Event Store

Append-only запись событий

4

Broker/Log

Fan-out в потребителей

5

Consumers

Проекции, интеграции, side effects

CQRS: write-path и read-path

Оба пути отображаются параллельно и запускаются независимо.

write-path (commands)

1

Client

POST/PUT command

2

Command Model

Инварианты и бизнес-правила

3

Event Stream

Факты домена

4

Projector

Обновляет read-модель

read-path (queries)

1

Client

GET query

2

Query API

Read-only endpoint

3

Read Model

Денормализованная проекция

Saga: coordination styles

Оркестратор централизованно решает, какой шаг выполнить следующим и когда запускать компенсации.

Все ключевые команды и решения проходят через один central coordinator.

Order Service

OrderCreated

Orchestrator
Orchestrator

ReserveFunds

Payment Service
Payment Service

PaymentReserved

Orchestrator
Orchestrator

ReserveStock

Inventory Service
Inventory Service

InventoryReserved

Orchestrator
Orchestrator

CreateShipment

Shipping Service
Shipping Service

ShipmentCreated

Orchestrator
Orchestrator

OrderCompleted

Order Service
Ключевая идея: один центральный orchestrator принимает решения и управляет вызовами сервисов.

Related

Microservice Patterns

Отдельная глава про integration и распределенные транзакции.

Открыть главу

Event Sourcing + CQRS + Saga

Event Sourcing

State хранится как последовательность событий, а не как последнее значение.

Когда применять

  • Нужен полный аудит и воспроизводимость изменений.
  • Важно пересчитывать проекции по истории событий.
  • Домен естественно выражается через события.

Trade-offs

  • Сложнее миграции event schema и versioning.
  • Нужны snapshot и replay-стратегии для производительности.

CQRS

Разделение write-модели (commands) и read-модели (queries).

Когда применять

  • Read/write профили сильно различаются.
  • Нужны отдельные read-оптимизированные проекции.
  • Система растет и требует независимого масштабирования путей.

Trade-offs

  • Увеличивается операционная сложность и число компонентов.
  • Read-model часто eventual consistent относительно write-model.

Saga

Управление распределенной транзакцией через цепочку локальных шагов + компенсации.

Когда применять

  • Операция затрагивает несколько сервисов/хранилищ.
  • 2PC недоступен или непрактичен.
  • Нужен контролируемый rollback через compensating actions.

Trade-offs

  • Нужно проектировать идемпотентность и повторные доставки.
  • Сложнее отладка долгоживущих и частично завершенных процессов.

Related Book

Software Architecture: The Hard Parts

Подробный разбор trade-offs, distributed workflows, orchestration/choreography и практики Saga.

Открыть главу

Saga: стили координации

Choreography

Сервисы подписываются на события и реагируют без центрального координатора.

Плюсы

  • Слабая связность
  • Меньше центральных bottleneck

Риски

  • Сложнее трассировка end-to-end
  • Риск «event spaghetti»

Orchestration

Оркестратор явно управляет шагами и компенсациями.

Плюсы

  • Прозрачный flow и observability
  • Проще верифицировать бизнес-процесс

Риски

  • Центральная точка сложности
  • Нужно масштабировать orchestration layer

Матрица выбора

ПотребностьРекомендацияПочему
Полный audit trail и replayEvent SourcingИстория изменений - first-class data.
Отдельные read/write SLACQRSНезависимая оптимизация и масштабирование read/write-path.
Distributed transaction без 2PCSagaЛокальные транзакции + компенсирующие действия.
Простая CRUD-система без высокой сложностиНе форсировать EDAОперационная сложность может быть выше бизнес-выгоды.

Частые ошибки

  • Пытаться внедрить все паттерны сразу без явного SLA и bounded context.
  • Считать событие «фактом», но публиковать в него технический шум без бизнес-смысла.
  • Не делать идемпотентность consumer-ов при at-least-once доставке.
  • Игнорировать schema versioning и обратную совместимость событий.
  • Не отслеживать DLQ, lag, duplicate rate и время завершения saga.

Related

Паттерны отказоустойчивости

DLQ дополняет retry/backoff и ограничивает каскадные сбои в consumer-пайплайнах.

Открыть главу

Dead Letter Queue (DLQ)

DLQ - это карантин для сообщений, которые не удалось обработать после retry. Цель DLQ - не «спрятать» ошибку, а сохранить проблемные события для безопасного разбора и контролируемого повторного запуска.

Когда отправлять в DLQ

Когда message превышает лимит попыток, нарушает schema contract или consistently падает из-за data issues.

Что сохранять

`messageId`, `attempts`, `lastError`, `originalTopic`, `failedAt`, версию контракта и ссылку на payload.

Что делать после

Делать triage, фиксить root cause и запускать re-drive batch с контролем rate limit и idempotency.

Практический checklist для DLQ

  • Выделяйте отдельный DLQ на каждый критичный поток, чтобы не смешивать домены и приоритеты.
  • Храните причину ошибки, число попыток, исходный topic/queue и payload reference для расследования.
  • Разделяйте transient и non-transient ошибки: не всё должно попадать в DLQ после одинакового числа retry.
  • Настройте re-drive процесс: ручной или автоматический возврат сообщений после исправления причины сбоя.
  • Добавьте алерты на скорость роста DLQ и SLA по времени разбора poison messages.

Мини-чеклист внедрения

1. Зафиксируйте event contract и policy versioning.
2. Обеспечьте idempotency для producer/consumer.
3. Настройте DLQ, retry policy и poison-message handling.
4. Мониторьте lag, replay time и success-rate saga.

Практический подход: сначала введите события для 1-2 критичных процессов, добавьте наблюдаемость и правила ретраев, и только после этого масштабируйте EDA на остальные bounded contexts.

Сначала контракт и операционная дисциплинапотом масштабирование паттерна.

Связанные главы

Чтобы отмечать прохождение, включи трекинг в Настройки