Событийная архитектура ценна не абстрактным обещанием слабой связности, а тем, что позволяет разнести принятие решения, публикацию факта и реакцию зависимых сервисов во времени.
Глава раскладывает хранение состояния через события, разделение команд и чтения и долгие бизнес-процессы на отдельные инженерные решения, чтобы было видно, где асинхронность действительно упрощает систему, а где она приносит эволюцию схем, повторное воспроизведение, лаги согласования и более дорогую эксплуатацию.
В инженерных обсуждениях это помогает спокойно разбирать оркестрацию и хореографию, границы событий, зависающие процессы и цену асинхронности без наивной идеи, что один брокер автоматически сделает систему гибче.
Практическая польза главы
Контракты событий
Проектируйте события как устойчивые бизнес-факты с совместимой эволюцией схем, а не как случайные транспортные пакеты.
Координация шагов
Осознанно выбирайте между оркестрацией и хореографией и заранее фиксируйте, где нужен явный управляющий контур.
Повторы и проблемные сообщения
Планируйте повторное воспроизведение, и идемпотентность обработчиков так, чтобы сбой не превращался в потерю данных.
Аргументация выбора
Объясняйте, когда события действительно снижают связанность, а когда они только добавляют лаги и операционную сложность.
Источник
Martin Fowler: Event Sourcing
Классический текст о том, зачем хранить историю состояния через события и где этот подход действительно полезен.
окупается не из-за самого брокера, а из-за того, что момент принятия решения и момент реакции на него больше не обязаны совпадать во времени. Эта развязка даёт масштабируемость и слабую связность, но цена за неё конкретная: события, контракты и восстановление после сбоев приходится проектировать заранее, иначе развязка превращается в источник скрытых отказов.
Базовые принципы событийной архитектуры
В этой главе мы отдельно разберём , и . Это не обязательный комплект на все случаи, а три разных инструмента, которые по-разному помогают управлять историей изменений, нагрузкой на чтение и запись и длинными бизнес-процессами.
Прежде чем выбирать инструмент, стоит закрыть базовый словарь — иначе спор уходит в термины, а не в решение. Договоритесь, что считается , как устроены и , когда требуется и какую роль играют , и , и где действительно нужен . Если система живёт с , доставкой , , , и , то эти решения нужно принимать отдельно внутри каждого , а не размазывать один и тот же шаблон на всю систему.
Событие фиксирует факт
Событие описывает то, что уже произошло в домене, и не должно меняться задним числом.
Асинхронность разрывает связность
Производители и потребители могут развиваться отдельно, если контракт события остаётся стабильным.
Согласование приходит не мгновенно
Между записью и итоговым видом данных есть лаг. Если продукт и API не закладывают его явно, пользователь увидит «свой» заказ ещё не обновлённым — и решит, что система потеряла данные.
Визуализация потоков данных
Ниже показаны три характерных сценария: базовый событийный поток, разделение чтения и записи в и координация шагов в .
Анимации потоков данных
Каждую схему можно запускать отдельно кнопкой «Старт».
Базовый поток событий
API команд
Принимает бизнес-команду
Доменный агрегат
Проверяет инварианты и принимает решение
Хранилище событий
Последовательно записывает события
Брокер или журнал
Раздаёт события потребителям
Потребители
Строят проекции, интеграции и побочные действия
CQRS: путь записи и путь чтения
Обе ветки показаны параллельно и запускаются независимо.
Путь записи
Клиент
Отправляет команду POST или PUT
Модель команд
Применяет правила и инварианты
Поток событий
Фиксирует факты домена
Проектор
Обновляет модель чтения
Путь чтения
Клиент
Отправляет запрос GET
API чтения
Отдаёт только данные для чтения
Модель чтения
Хранит денормализованную проекцию
Saga: стили координации
Отдельный координатор решает, какой шаг запускать дальше и когда начинать компенсации.
Все ключевые команды и решения проходят через одного координатора.
OrderCreated
ReserveFunds
PaymentReserved
ReserveStock
InventoryReserved
CreateShipment
ShipmentCreated
OrderCompleted
Связанная глава
Microservice Patterns
Отдельная глава про интеграционные паттерны, паттерн Saga и границы распределённых транзакций.
Хранение состояния через события (Event Sourcing), разделение команд и чтения (CQRS) и паттерн Saga (saga pattern)
Event Sourcing
Состояние восстанавливается из последовательности событий, а не хранится только как последнее значение.
Когда применять
- Нужен полный журнал аудита и воспроизводимая история изменений.
- Важно пересчитывать проекции или строить новые представления данных по истории событий.
- Предметная область естественно описывается через факты, которые произошли во времени.
Компромиссы
- Сложнее эволюция схемы событий и миграции старой истории.
- Нужны снимки состояния и стратегия повторного воспроизведения, чтобы не замедлить систему.
CQRS
Путь записи и путь чтения разделяются, чтобы каждый из них оптимизировать под свою нагрузку и модель данных.
Когда применять
- Нагрузка на чтение и запись заметно различается.
- Нужны отдельные проекции, оптимизированные под быстрые чтения.
- Система выросла настолько, что пути чтения и записи выгодно масштабировать независимо.
Компромиссы
- Увеличиваются операционная сложность и число компонентов.
- Модель чтения часто сходится с путём записи не мгновенно, а с лагом.
Saga
Распределённая операция раскладывается на локальные шаги и компенсирующие действия вместо одной общей транзакции.
Когда применять
- Одна бизнес-операция затрагивает несколько сервисов или хранилищ.
- Двухфазная фиксация (two-phase commit) недоступна или слишком дорога по задержке и связанности.
- Нужно управляемо откатывать частично завершённый процесс через компенсации.
Компромиссы
- Каждый шаг и каждая компенсация должны переживать повторы без двойного эффекта.
- Отладка долгоживущих и частично завершённых процессов становится сложнее.
Связанная книга
Software Architecture: The Hard Parts
Подробный разбор инженерных компромиссов, распределённых процессов и практики применения Saga.
Saga: стили координации
делает процесс прозрачнее и централизует решения, а сильнее ослабляет связанность, но сложнее для трассировки и отладки.
Хореография
Сервисы подписываются на события и сами запускают следующий шаг без центрального координатора.
Плюсы
- Слабее связность между сервисами
- Нет единой управляющей точки, которая станет узким местом
Риски
- Сложнее проследить весь путь операции целиком
- Легко получить хаотичную сеть событий без явных границ
Оркестрация
Отдельный координатор явно решает, какой шаг выполнять дальше и когда запускать компенсации.
Плюсы
- Проще наблюдать процесс целиком
- Легче проверять бизнес-логику и правила переходов
Риски
- Появляется центральная точка сложности
- Контур оркестрации нужно отдельно масштабировать и защищать
Как выбирать паттерн
| Задача | Что выбрать | Почему |
|---|---|---|
| Нужна полная история изменений и восстановление состояния | Event Sourcing | История становится первичными данными, а не побочным журналом. |
| К чтению и записи предъявляются разные требования по уровню сервиса и профилю нагрузки | CQRS | Можно отдельно оптимизировать и масштабировать путь чтения и путь записи. |
| Одна операция охватывает несколько сервисов без общей транзакции | Saga | Локальные шаги и компенсации дают контролируемое завершение процесса. |
| Система остаётся простой CRUD-моделью без сложных интеграций | Не навязывать событийную архитектуру | Операционная сложность может оказаться выше реальной бизнес-пользы. |
Частые ошибки
- Внедрять хранение состояния через события (Event Sourcing), разделение команд и чтения (CQRS) и паттерн Saga (saga pattern) одновременно без явной проблемы, которую нужно решить.
- Публиковать в события технический шум вместо фактов предметной области.
- Не проектировать идемпотентность обработчиков при доставке как минимум один раз.
- Оставлять эволюцию схемы событий без политики обратной совместимости.
- Не следить за проблемными сообщениями, лагом обработки, скоростью роста дублей и временем завершения процесса: паттерн Saga (saga pattern).
Связанная глава
Паттерны отказоустойчивости
Очередь ошибочных сообщений дополняет повторы, тайм-ауты и изоляцию сбоев в контурах асинхронной обработки.
Очередь ошибочных сообщений
нужна не для того, чтобы спрятать сбой, а чтобы безопасно изолировать проблемные сообщения, разобраться в причине и вернуть их в поток контролируемо. Она особенно важна там, где обработчик работает с внешними зависимостями, нестабильными данными или дорогими побочными эффектами.
Когда изолировать сообщение
Сообщение исчерпало лимит повторов, нарушает контракт события или стабильно падает из-за самих данных — повторять его дальше бессмысленно, оно только держит партицию.
Что сохранять
Идентификатор сообщения, число попыток, последнюю ошибку, источник, время сбоя и ссылку на полезную нагрузку.
Что делать дальше
Разобрать причину, исправить её и вернуть сообщения в основной поток с контролем скорости и идемпотентности.
Практический чеклист для проблемных сообщений
- Выделяйте отдельную очередь ошибочных сообщений для каждого критичного потока, чтобы не смешивать домены и приоритеты.
- Сохраняйте причину ошибки, число попыток, исходный топик или очередь и ссылку на полезную нагрузку для расследования.
- Разделяйте временные и постоянные ошибки: не все сообщения должны уходить в изоляцию после одинакового числа повторов.
- Настройте процесс возврата сообщений в основной поток после исправления причины сбоя: вручную или автоматически.
- Следите за скоростью роста проблемных сообщений и договоритесь о целевом времени их разбора.
Проверки перед внедрением
Практический подход: начните с одного-двух критичных процессов, сначала зафиксируйте контракт, наблюдаемость и правила повторов, и только потом масштабируйте событийную модель на остальные контуры системы.
Связанные главы
- Консистентность и идемпотентность - помогает проектировать обработку повторных событий, паттерны outbox и inbox и корректность при доставке как минимум один раз.
- Паттерны устойчивости: размыкатель цепи, изоляция и повторы - дополняет событийную архитектуру правилами повторов, тайм-аутами и управляемой деградацией обработчиков.
- Distributed Message Queue - показывает практическую архитектуру очередей, разбиение на партиции, гарантии порядка и пределы пропускной способности событийных потоков.
- Паттерны взаимодействия между сервисами - сравнивает синхронные и асинхронные интеграции и помогает понять, где события лучше прямого удалённого вызова процедуры.
- Принципы проектирования масштабируемых систем - объясняет компромиссы между задержкой и пропускной способностью и показывает, как работать с обратным давлением в событийных системах.
- Kafka: The Definitive Guide, 2nd Edition (short summary) - даёт фундамент по журналам событий, партиционированию, сценариям доставки и эксплуатационным практикам вокруг Kafka.
- Microservice Patterns (short summary) - углубляет темы паттерна Saga, распределённых транзакций и интеграционных шаблонов для микросервисов.
