Когда интеграция перестаёт укладываться в схему «сервис A вызвал сервис B и дождался ответа», на проекте появляется событийно-ориентированная архитектура (Event-Driven Architecture, EDA) — стиль, в котором системы обмениваются фактами о произошедшем, а не командами и синхронными запросами. Для системного аналитика EDA — способ описать границы контекстов, контракты событий и ответственность за реакции; без этого модель интеграции остаётся неполной даже при детальных REST-спецификациях.

1. Суть темы

Event-Driven Architecture (EDA) — архитектурный стиль построения и интеграции распределённых систем, при котором компоненты публикуют события о значимых изменениях состояния, а другие компоненты асинхронно реагируют на них, часто через брокер сообщений или шину событий (event bus). Событие фиксирует то, что уже произошло в домене (OrderPlaced, PaymentConfirmed), а не приказ «сделай X». На уровне системной и интеграционной архитектуры EDA — один из стилей связи компонентов наряду с синхронными API и пакетной интеграцией.

EDA — не замена синхронным API, а дополнительный слой интеграции:

Синхронный API (request–response)Event-Driven (publish–subscribe)
Клиент ждёт ответ в рамках вызоваProducer не знает всех потребителей
Подходит для query и команд с немедленным результатомПодходит для побочных эффектов (side effects), уведомлений, разветвления (fan-out)
Жёсткая связность «кто кому звонит»Слабая связность через контракт события
Сильная согласованность в момент ответаСогласованность «в конечном счёте» (eventual consistency)

Request–reply поверх messaging (reply topic, RPC-over-Kafka) — транспортный приём, не доменное событие; для SA это гибрид с 03.05.02 Синхронные протоколы (REST, gRPC, GraphQL), а не замена EDA.

EDA среди видов интеграции (подробнее — 03.05.01 Виды интеграции (точка-точка, ESB, API-led)):

Контекст интеграцииРоль EDAКогда достаточно без «полной» EDA
API-led (experience / process / system API)События — слой system API для фактов; REST/gRPC остаётся для команд и queryОдин consumer на очередь, без каталога и fan-out — point-to-point асинхронность из 03.05.04 Асинхронное взаимодействие (очереди, Kafka, Events)
ESB / integration hubЦентральная шина маршрутизирует и трансформирует события; каталог и governance критичныПростой маршрут «A → B» через ESB без доменного каталога — транспорт, не EDA как стиль
Event mesh / domain eventsМножество bounded context публикуют факты; слабая связность, хореография—
Point-to-point очередьДопустима как транспорт одного события между двумя сервисамиНет каталога, одного producer и одного consumer — EDA как архитектурный стиль избыточна

Граница этой статьи:

В зоне этой статьиВынесено в соседние заметки
EDA как архитектурный стиль; события, хореография vs оркестрацияТранспорт, брокеры, семантика доставки — 03.05.04 Асинхронное взаимодействие (очереди, Kafka, Events)
Контракты событий, каталог, моделирование потоковAsyncAPI, CloudEvents, JSON Schema — 03.05.03 Спецификации контрактов (OpenAPI, AsyncAPI, Protobuf)
Синхронные операции query/command03.05.02 Синхронные протоколы (REST, gRPC, GraphQL)
Топологии интеграции (ESB, API-led, точка-точка)03.05.01 Виды интеграции (точка-точка, ESB, API-led)
Saga, CQRS, event sourcing как паттерны03.06.01 Архитектурные паттерны распределённых систем
Bounded context, доменный язык03.06.02 Основы Domain-Driven Design
flowchart LR
    subgraph producer [Producer / bounded context]
        SVC[Сервис заказов]
        DB[(БД)]
        SVC --> DB
        SVC -->|publish| BUS
    end
    subgraph bus [Event bus / broker]
        BUS[Топик order.events]
    end
    subgraph consumers [Consumers]
        WH[Склад]
        BILL[Биллинг]
        AN[Аналитика]
    end
    BUS --> WH
    BUS --> BILL
    BUS --> AN

На 03.01.04 Интерфейсы и интеграционные взаимодействия EDA добавляет канал событий рядом с синхронными API: REST/gRPC для чтения и команд с немедленным ответом; события — для распространения фактов и запуска реакций в других контекстах.

2. Ключевые понятия

Событие и его отличие от команды

Событие (event) — уведомление о произошедшем факте. Имена — в прошедшем времени доменного языка: OrderPlaced, не PlaceOrder. Команда (command) — просьба выполнить действие.

Scope этой заметки — доменные факты (facts). Команда принимается внутри bounded context или через синхронный API на границе, где нужен немедленный ответ (POST /orders → 201 Created). Межконтекстово публикуют факт: OrderPlaced. Асинхронные команды (отдельный топик «сделай X») — отдельный контракт; см. 03.05.04 Асинхронное взаимодействие (очереди, Kafka, Events).

Один бизнес-факт из разных контекстов может иметь разные имена (OrderPaid vs PaymentSettled). В 03.06.02 Основы Domain-Driven Design и 10.01.14 Шаблон глоссария и словаря данных зафиксируйте каноническое имя события и маппинг между контекстами — иначе трассируемость ломается.

CDC-событие (изменение строки в БД) ≠ доменное событие (бизнес-факт). Технический поток из change data capture — см. 03.05.04 Асинхронное взаимодействие (очереди, Kafka, Events) и 03.03.02 Логическая и физическая модель данных; в каталоге держите отдельно.

Два распространённых способа наполнения события:

ПаттернСутьКогда выбирать
Уведомление о событии (event notification)Минимальный payload (id, тип); детали consumer добывает отдельно (API, read model)Много подписчиков с разными потребностями в данных
Перенос состояния в событии (event-carried state transfer)Payload достаточен для обработки без дополнительного запросаНужна автономность consumer, допустимо дублирование данных

При event notification синхронный вызов API для деталей не отменяет eventual consistency: read model consumer может отставать от момента публикации. В спецификации зафиксируйте источник правды для экрана: либо синхронный GET сразу после POST (ответ команды — актуальное состояние, события — только для фоновых реакций), либо проекция consumer (UI читает read model, обновляемую по событиям, с явным состоянием «ожидание синхронизации»). Смешивать оба канала как «оба истина» без правила — типичный источник расхождения требований.

Контракт события (event schema / contract) — структура payload, версия, тип события, метаданные: eventId, eventType, occurredAt, aggregateId, correlationId, causationId. Для обёртки метаданных по умолчанию используйте CloudEvents — поля контракта маппятся на id, type, time, source, subject, datacontenttype (см. раздел источников).

Канал доставки

EDA опирается на Pub/Sub, очереди или event streaming — механика транспорта, retention, replay, consumer groups описана в 03.05.04 Асинхронное взаимодействие (очереди, Kafka, Events). Здесь SA фиксирует только требование к каналу (разветвление (fan-out) vs конкурирующие потребители (competing consumers), порядок по ключу, допустимость дубликатов) и бизнес-последствия сбоя доставки.

Хореография vs оркестрация

ПодходКоординаторПлюсыРиски
Хореография (choreography)Нет центрального; каждый сервис реагирует на событияСлабая связность, простое добавление подписчиковНеявный граф зависимостей, «spaghetti» без каталога
Оркестрация (orchestration)Центральный процесс (Saga orchestrator, BPMN engine)Явная модель шагов, проще расследоватьЕдиная точка отказа, жёстче связность

На практике смешивают: локальная хореография внутри домена + оркестрация сквозного процесса через 03.04.01 BPMN 2.0 - введение и основы (message throw/catch, correlation). Имя доменного события в каталоге должно совпадать с message в BPMN; типы событий BPMN (start, intermediate, boundary) — см. 06.02.01 Потоки, шлюзы, события — не подменяют доменные facts. Критерий выбора для SA: нужна ли явная видимость сквозного процесса для бизнеса и комплаенса — тогда оркестратор; достаточно независимых реакций — хореография с каталогом событий.

Связанные паттерны — что означает для модели

Эти паттерны не тождественны EDA; механика и композиция — в 03.06.01 Архитектурные паттерны распределённых систем. Таблица ниже описывает смысл свойства для архитектурной модели, а не место в артефактах:

Свойство / паттернЧто означает для модели EDA
Атомарность «commit в БД + publish события» (transactional outbox)Исключить фантомные и потерянные события
Идемпотентность consumer при at-least-onceПовторная доставка не дублирует эффект
Очередь недоставленных (DLQ) и политика retryИзоляция необрабатываемых сообщений
Компенсация при частичном сбое SagaСогласованность сквозного процесса

Event sourcing, CQRS, Saga как архитектурные решения — отдельные ADR, не обязательное следствие выбора EDA.

Нефункциональные свойства модели

Таблица фиксирует что означает свойство для EDA и какие решения закладывать в модель. Перенос в формулировки NFR и шаблоны — в §3.

СвойствоСмысл для модели EDA
Eventual consistencyRead models временно расходятся; нужны user-visible states «pending» и правило источника правды для UI
Ordering (упорядоченность)Порядок гарантируется в пределах partition key (например, orderId); события без общего ключа не упорядочены — типичный источник гонок (billing + inventory)
Delivery semantics (семантика доставки)At-least-once — норма; consumer обязан быть идемпотентным. At-most-once — допустима потеря. Exactly-once end-to-end на практике сводится к at-least-once + идемпотентность + dedup по eventId; границу (брокер vs consumer vs БД) уточняйте отдельно
Latency end-to-endp95 от публикации до завершения всех критичных реакций
Retention / replayСрок хранения журнала, RPO/RTO, политика replay — бизнес-решение; механика — 03.05.04 Асинхронное взаимодействие (очереди, Kafka, Events)
Observability (наблюдаемость)Трассировка по correlationId / causationId через все consumers
Audit trail (журнал аудита)История фактов в потоке событий; для регуляторики — явные NFR: неизменяемость (immutability), срок хранения, доступ — см. 08.06 Юридические и регуляторные требования РФ (PCI-DSS)

3. Как применяется в работе аналитика

Каталог доменных событий

Базовый артефакт EDA — каталог событий, понятный и бизнесу, и команде. Минимальные колонки:

ПолеСодержание
Имя событияPast tense, ubiquitous language: OrderPaid; каноническое имя из глоссария
ProducerBounded context / единственный источник истины для факта
ConsumersКто реагирует и какая бизнес-реакция; уточните fan-out (Pub/Sub, каждый получает копию) vs competing consumers (очередь, один воркер обрабатывает сообщение)
КритичностьBlocking — задержка или сбой реакции блокирует UX, SLA, компенсацию, saga или ручной workflow; best-effort — допустимы пропуск и отложенная обработка (аналитика, уведомления). Blocking ≠ обязательно saga: это может быть синхронная блокировка UI или ручной эскалационный процесс без оркестратора
DeliveryПовторы, порядок по ключу, допустимость дубликатов
КонтрактСсылка на AsyncAPI message / schema version
GovernanceКто утверждает новое событие; процесс review на дубли имён и топиков — 02.05.02 Управление изменениями (Change Control Board) или процесс изменения контрактов из 03.05.07 Управление версионированием API и жизненным циклом

Каталог связывает 03.04.01 BPMN 2.0 - введение и основы («после оплаты…») с техническим топиком и не дублирует детальную спецификацию из 03.05.03 Спецификации контрактов (OpenAPI, AsyncAPI, Protobuf).

Контракт события на логическом уровне

В спецификации или 10.01.06 Шаблон спецификации Producer (Kafka, RabbitMQ) / 10.01.07 Шаблон спецификации Consumer (Kafka, RabbitMQ) зафиксируйте:

  1. Payload — поля, типы, обязательность; трассировка к 03.03.01 ER-диаграммы (сущность-связь) и 03.03.03 Словарь данных (описание); при multi-tenant — tenantId в payload и/или partition key. Минимизируйте PII в payload: при event-carried state переносите только необходимые поля; согласуйте с 03.05.05 Безопасность API единую модель идентичности producer/consumer, а не «анонимную шину».
  2. Метаданные — eventId (уникальность каждой публикации), eventType, occurredAt, aggregateId, correlationId, causationId. Обёртка CloudEvents — default для межсервисных событий; поля контракта явно маппятся на атрибуты спецификации.
  3. Дедупликация — eventId для dedup на стороне consumer; ключ идемпотентности (idempotency key) (часто aggregateId + бизнес-операция) — что consumer хранит, чтобы повтор не изменил состояние дважды.
  4. Версионирование — additive fields без breaking change; новый eventType vs новая версия schema; период двойной записи/чтения (dual-write/dual-read); политика breaking changes — 03.05.07 Управление версионированием API и жизненным циклом.
  5. Классификация — event vs command на границе; PII и retention (GDPR).

Пример строки контракта:

АтрибутТипОбяз.ИсточникПримечание
orderIdUUIDдаOrder aggregateключ партиционирования
paidAtdatetime (ISO 8601)даPayment service
amountdecimal(18,2)даPayment serviceвалюта — отдельное поле

Моделирование потоков событий

Event Flow / Integration View — диаграмма «кто кого будит» через какие каналы. На 03.04.02 Диаграмма потоков данных (DFD) показывают потоки данных между процессами и хранилищами; event flow дополняет DFD асинхронными триггерами — какой факт запускает какую реакцию, без дублирования детальной спецификации payload.

На 03.02.03 Диаграмма последовательности (Sequence Diagram) для асинхронных сообщений:

  • стрелка async (open arrowhead или пометка «async»);
  • отсутствие обязательного return;
  • correlationId в сообщении;
  • альтернативные фрагменты: таймаут, retry, DLQ.

На 03.02.04 Диаграмма состояний (State Machine Diagram) переходы должны совпадать по имени с типами доменных событий из каталога — иначе трассируемость ломается на стыке BA и разработки.

На 03.02.01 Диаграмма прецедентов (Use Case Diagram) триггер-событие из другого контекста моделируйте как include/extend или актор «внешняя система через шину» — прецедент «обработать PaymentConfirmed» явно связывает use case с записью каталога.

На 03.01.02 Моделирование контекста системы (Context Diagram) внешние системы могут быть и producer, и consumer событий через шину — не только REST-клиентами.

sequenceDiagram
    participant Order as Order Service
    participant Bus as Event Bus
    participant Pay as Payment Service
    participant WH as Warehouse

    Order->>Bus: OrderPlaced (correlationId: c-123)
    Bus->>Pay: OrderPlaced
    Pay->>Bus: PaymentConfirmed (correlationId: c-123)
    Bus->>WH: PaymentConfirmed
    Note over Order,WH: Пример политики «отгрузка после оплаты»; альтернатива — реакция на OrderPlaced

Вопросы на discovery

Задайте их до фиксации архитектуры в 10.01.13 Шаблон архитектурного решения (ADR):

  • Какое событие — источник истины для факта? Кто единственный producer? Какое каноническое имя в глоссарии?
  • Источник правды для экрана — ответ синхронной команды, GET после POST или read model consumer?
  • Нужна ли строгая упорядоченность по сущности (orderId)? Какие события не должны предполагать порядок друг относительно друга?
  • Допустима ли повторная обработка? Как обеспечивается идемпотентность (eventId, idempotency key)?
  • Как пользователь видит промежуточное состояние при eventual consistency?
  • Граница: оркестратор процесса vs чистая реакция на события?
  • Что происходит при частичном сбое Saga — компенсация, ручная обработка, DLQ? См. 08.05 Управление рисками на этапе анализа.

Согласование EDA с API-дизайном

Практическое правило на проекте:

  • API-first для команд и query — синхронный REST/gRPC/GraphQL там, где клиенту нужен немедленный ответ или валидация.
  • Contract-first для событий — AsyncAPI, JSON Schema/Avro, CloudEvents; события не «дописываются потом».

Сценарий «создать заказ» часто выглядит так: синхронная команда POST /orders → 201 Created → асинхронный факт OrderPlaced → реакции склада, аналитики, уведомлений. SA описывает оба канала, правило источника правды для UI и связь через correlationId.

Нефункциональные требования в артефактах

Свойства из таблицы §2 переносите в 10.01.12 Шаблон нефункциональных требований (NFR) или матрицу интеграций — классификация NFR: 08.02 Нефункциональные требования (НФТ) — классификация и стандарты. Для каждой строки каталога событий укажите измеримое значение (не повторяйте определение свойства): p95 latency для blocking-цепочек, RPO/RTO журнала, retention топика, max retry до DLQ, lag consumer groups, immutability audit trail. UX-состояния «pending» — в 02.03.03 Спецификация требований (SRS, Use Case, User Story).

Критерии выбора EDA

Подходит, когда:

  • один факт должен разойтись к многим подписчикам без изменения producer;
  • команды слабо связаны, новые интеграции добавляются часто;
  • нужен audit trail с явными NFR (retention, immutability) — при регуляторных требованиях см. 08.06 Юридические и регуляторные требования РФ (PCI-DSS);
  • пиковые нагрузки буферируются через брокер;
  • допустима согласованность «в конечном счёте».

С осторожностью, когда:

Артефакты, которые отдаёт аналитик

  1. Каталог доменных событий (таблица выше) с governance.
  2. AsyncAPI или логический контракт с примерами payload и маппингом CloudEvents.
  3. Диаграмма потоков событий + фрагменты sequence/state/use case для ключевых сценариев.
  4. Сценарии ошибок — дубликат, out-of-order, partial failure, DLQ, ручной replay.
  5. Матрица трассировки — требование ↔ событие ↔ consumer ↔ тест; 02.03.04 Трассировка требований (концепция); 10.01.15 Шаблон матрицы трассировки.
  6. Критерии приёмки — 02.04.02 Критерии приёмки (Acceptance Criteria): «given event OrderPlaced published → within N sec consumer effect visible»; повторная доставка не дублирует побочный эффект.
  7. Contract testing — владелец контракта (consumer-driven vs provider-driven) и матрица «producer schema ↔ consumer expectations»; техники статической проверки — 08.03 Тестирование требований (статическое тестирование).

4. Типовые ошибки и ограничения

Ограничения EDA

Eventual consistency — read models временно расходятся; без UX-правил «ожидание обработки» в 02.03.03 Спецификация требований (SRS, Use Case, User Story) пользователь видит противоречия (оплаченный заказ ещё «в обработке»).

Сложность observability — без correlationId / causationId расследование инцидента в цепочке из пяти consumers резко дорожает.

Неявные зависимости при хореографии — граф «событие A → B → C → D» не виден в коде одного сервиса; без каталога и диаграммы команда теряет контроль.

Версионирование контрактов — breaking change в payload ломает всех consumer; нужна политика evolution и согласованный review через 03.05.07 Управление версионированием API и жизненным циклом.

Ordering и causality — ошибочное предположение о глобальном порядке событий приводит к race conditions; порядок — только в пределах partition key.

Replay — повторное проигрывание журнала вызывает побочные эффекты (side effects), если consumer не идемпотентен.

Regulatory / PII — события «размножают» персональные данные по топикам; retention и право на удаление сложнее, чем в одной БД; минимизация payload обязательна.

Типичные ошибки аналитика

ОшибкаПоследствиеЧто делать
Путать command и eventНарушение границ контекстов, скрытая связностьКоманда — sync API или внутри контекста; между контекстами — facts
Chatty events без бизнес-смыслаШум, нагрузка, сложность схемОдно событие = значимый доменный факт
God event с избыточным payloadЖёсткая связность, хрупкие контрактыEvent notification или узкий event-carried state
Нет eventId / idempotency key в контрактеДубликаты при at-least-onceЯвная семантика dedup в 10.01.07 Шаблон спецификации Consumer (Kafka, RabbitMQ)
NFR exactly-once без границыНереализуемое требованиеAt-least-once + идемпотентность + dedup
Публикация до commit БДФантомные или потерянные событияТребование transactional outbox — 03.06.01 Архитектурные паттерны распределённых систем
Нет DLQ и политики retryБесконечный redelivery, блокировка очередиNFR + сценарий ручной обработки
Описан только основной сценарий (happy path)Production gaps: таймауты, out-of-order, partial failureАльтернативные потоки на sequence и в 02.04.02 Критерии приёмки (Acceptance Criteria)
EDA «везде»Избыточная сложностьСинхронный API или point-to-point там, где достаточно
Имена событий не совпадают с BPMN/state diagramРасхождение артефактовЕдиный 10.01.14 Шаблон глоссария и словаря данных, review на трассируемость
Два источника правды для UIREST «актуально», события «истина»Явное правило в спецификации (см. event notification)
SA не фиксирует источник истиныДва producer одного факта или дубли имёнОдин owner на каноническое событие; маппинг в глоссарии
Blocking без расшифровкиНевозможно трассировать в AC и SLAUX-блок, saga, compensation или manual workflow; blocking ≠ saga

EDA не снимает 03.05.05 Безопасность API: аутентификация producer/consumer, шифрование in transit, ACL на топики, минимизация PII в payload — часть интеграционного контракта; см. 08.07 Основы кибербезопасности для системного аналитика.

Связи с другими разделами базы

Источники и дополнительные материалы

Статьи и обзоры архитектуры

Спецификации и паттерны интеграции