«Событийная архитектура» — это зонтичный термин, под которым прячется десяток разных вещей: брокеры сообщений, event sourcing, CQRS, saga, workflow-движки. Их часто валят в одну кучу и продают как единое решение «сделаем всё асинхронным — и станет хорошо». Не станет: это разные инструменты для разных задач, с разной ценой.
Эта статья — карта. Она не учит ни одному паттерну в деталях, а расставляет понятия по местам: что такое событие, чем оно отличается от команды и сообщения, где в системе живёт брокер, где event store, а где workflow engine. Остальные выпуски серии разбирают каждый кусок отдельно — здесь даётся рамка, чтобы они складывались в картину, а не в список модных слов.
В статье
- Команда, событие, сообщение: три разных понятия
- Domain event vs integration event
- Где что живёт: брокер, event store, workflow engine
- Event-driven ≠ event sourcing
- Eventual consistency как данность, а не баг
- Карта серии: что читать дальше
- Как читать серию по ролям
Команда, событие, сообщение: три разных понятия
Три слова, которые постоянно путают:
- Команда (command) — намерение что-то изменить. Адресная, может быть отклонена. Глагол в повелительном наклонении:
AllocateReserve,ConsumeOxygen. У команды есть конкретный получатель, который решает, выполнять её или нет. - Событие (event) — факт, который уже произошёл. Неизменяемо, отклонить нельзя. Прошедшее время:
ReserveAllocated,OxygenConsumed. У события нет адресата — оно просто сообщает миру, что случилось; подписчиков может быть ноль, один или много. - Сообщение (message) — транспортная обёртка. И команда, и событие путешествуют по системе как сообщения через брокер или HTTP. «Сообщение» — про доставку, «команда/событие» — про смысл.
Ключевая разница команды и события — в направлении связности. Команда связывает отправителя с получателем («сделай это»). Событие развязывает: издатель не знает и не хочет знать, кто его слушает. Именно из этого свойства растёт слабая связанность event-driven систем — и их же сложность в отладке.
Domain event vs integration event
Не все события одинаковы. Полезно различать два уровня:
- Domain event — внутреннее событие одного сервиса/контекста. Описывает факт в терминах домена, живёт внутри границы сервиса, потребляется его же кодом (например, проекциями в event sourcing). Формат может меняться относительно свободно — у него один владелец.
- Integration event — событие, которое сервис публикует наружу, для других сервисов. Это публичный контракт. Его формат менять опасно: на той стороне есть потребители, которых вы не контролируете.
Смешивать их — частая ошибка. Если выставить наружу сырые domain event, любое внутреннее изменение модели ломает потребителей. Поэтому на границе обычно стоит трансляция: внутренние domain event → аккуратно версионированные integration event. Подробнее о том, как проектировать и эволюционировать публичные контракты, — в статье про контракты событий.
Где что живёт: брокер, event store, workflow engine
Три инфраструктурных компонента, которые легко перепутать, потому что все «про события»:
- Брокер сообщений (RabbitMQ, Kafka, NATS) — транспорт. Доставляет сообщения от издателя к потребителям. Его задача — передать и (иногда) подержать сообщение, пока его не заберут. Брокер не хранит состояние вашего домена.
- Event store (EventStoreDB, PostgreSQL как append-only) — хранилище событий как источника правды. Здесь события — это данные, из которых вычисляется состояние. Это сердце event sourcing, а не транспорт.
- Workflow engine (Temporal, Cadence) — координатор длительных процессов. Хранит состояние выполнения многошаговых workflow и гарантирует, что они доедут до конца, переживая перезапуски.
Одна и та же система может использовать все три одновременно — и это нормально. Путаница начинается, когда брокер пытаются использовать как event store («у нас же Kafka хранит события»), или когда самодельный state machine на таблице в БД пытаются дотянуть до возможностей workflow engine.
Event-driven ≠ event sourcing
Самая частая терминологическая ловушка серии, поэтому вынесем её отдельно:
- Event-driven architecture — про взаимодействие. Компоненты общаются через события вместо прямых вызовов. Это про связи между сервисами.
- Event sourcing — про хранение состояния. Состояние сервиса хранится как последовательность событий вместо текущего снимка. Это про то, как один сервис хранит свои данные.
Можно иметь event-driven систему без единого event-sourced сервиса (сервисы с обычными таблицами общаются через брокер). Можно иметь event-sourced сервис, который ни с кем не общается событиями. Это ортогональные вещи, которые удачно сочетаются, но не обязаны идти вместе.
Eventual consistency как данность, а не баг
Как только данные размазаны по нескольким хранилищам и синхронизируются через события, появляется задержка: read-модель отстаёт от write-модели, один сервис уже знает о факте, другой ещё нет. Это eventual consistency — не дефект реализации, а фундаментальное свойство распределённых систем.
С ней не «борются», её проектируют: где допустимо отставание в секунды, а где нужна строгая согласованность; где поможет «read your own writes»; как мониторить лаг. Эта тема — сквозная для всей серии и подробно разобрана в статье про согласованность данных.
Карта серии: что читать дальше
Серия движется от хранения состояния внутри сервиса — к доставке событий между сервисами — к координации длинных процессов — и заканчивается матрицей выбора:
- Эта статья — карта понятий.
- Event Sourcing — когда история важнее текущего состояния.
- CQRS — разделение моделей чтения и записи.
- Transactional Outbox/Inbox — как надёжно публиковать события без распределённой транзакции.
- Контракты событий — naming, версионирование, эволюция схем.
- Saga — распределённые транзакции без двухфазного коммита.
- Temporal — durable workflows вместо самодельных очередей.
- Матрица решений — когда какой паттерн выбрать.
Читать подряд необязательно: если вас интересует конкретная задача, начните с финальной матрицы выбора и идите к нужному выпуску. Но если событийная архитектура для вас в новинку — порядок выше выстроен так, чтобы каждый следующий паттерн опирался на предыдущие.
Как читать серию по ролям
Серия не чисто кодовая — у разных ролей разный интерес:
- Аналитику / продакту — эта карта, Event Sourcing (история как требование) и decision matrix. Достаточно, чтобы понимать язык и цену решений, не погружаясь в реализацию.
- Backend-разработчику — основной маршрут: Outbox/Inbox, контракты, Saga, Temporal. Здесь живёт инженерная механика.
- Архитектору — вся серия, с акцентом на границы и комбинации паттернов в матрице.
- Тимлиду — карта, decision matrix и разделы «когда не надо» в каждой статье: они про цену внедрения и готовность команды, а не про код.
Комментарии