.NET Tutorial
Middle5 мин чтенияВопрос 15 из 17

Что такое Event-Driven Architecture?

Команда против события

Команда Событие
Смысл «Сделай» (ReserveStock) «Произошло» (OrderPlaced)
Время Повелительное Прошедшее
Получатели Один конкретный Любое число (0..N)
Отправитель знает получателя Да Нет
Может быть отклонено Да Нет — это факт
Транспорт Очередь (point-to-point) Топик / exchange (pub/sub)

Поток событий

Orders:
publish OrderPlaced

Брокер
топик orders.events

Inventory:
зарезервировать

Notifications:
письмо клиенту

Loyalty:
начислить баллы

Analytics:
обновить витрины

Новый сервис Fraud
подключается без изменения Orders

Orders:
publish OrderPlaced

Брокер
топик orders.events

Inventory:
зарезервировать

Notifications:
письмо клиенту

Loyalty:
начислить баллы

Analytics:
обновить витрины

Новый сервис Fraud
подключается без изменения Orders

Производитель не знает о потребителях

Сравните с синхронным подходом: Orders вызывает по HTTP четыре сервиса, ждёт их ответов, знает их адреса и падает, если любой недоступен. В EDA оформление заказа завершается, как только событие надёжно опубликовано.

Стили событий

Event Notification

JSON
{ "type": "OrderPlaced", "orderId": "ord_1042", "occurredAt": "2025-09-30T10:15:00Z" }

Минимальное событие. Потребитель, которому нужны детали, запрашивает их у Orders API. Плюс — маленький контракт; минус — обратная связанность (запрос к источнику) и нагрузка на него.

Event-Carried State Transfer

JSON
{
  "type": "OrderPlaced",
  "orderId": "ord_1042",
  "customer": { "id": "c_7", "email": "ann@example.uz", "tier": "gold" },
  "items": [ { "sku": "PHONE-1", "qty": 1, "price": 7990000 } ],
  "total": 7990000,
  "occurredAt": "2025-09-30T10:15:00Z"
}

Событие несёт всё нужное — потребители не обращаются к источнику и могут хранить локальную копию данных (например, Shipping хранит адреса клиентов). Плюс — автономность; минусы — больше данных в сообщениях, дублирование, контракт шире.

Event Sourcing и CQRS

События — не только интеграция, но и способ хранения (см. вопросы про Event Sourcing и CQRS).

Топологии обработки

PaymentsInventoryБрокерOrdersOrderPlacedOrderPlacedStockReservedStockReservedPaymentCompletedPaymentCompleted → статусPaid
PaymentsInventoryБрокерOrdersOrderPlacedOrderPlacedStockReservedStockReservedPaymentCompletedPaymentCompleted → статусPaid
Хореография: процесс складывается из реакций
  • Broker topology / хореография — нет центрального координатора, каждый реагирует на события. Гибко, но поток процесса неявный.
  • Mediator topology / оркестрация — центральный процесс (сага, workflow) управляет шагами командами и слушает события.

Надёжность

Producer:
Outbox в той же транзакции

Брокер:
durable, подтверждения,
репликация

Consumer:
идемпотентность (Inbox),
ретраи, DLQ

Наблюдаемость:
traceparent в заголовках,
lag, DLQ-алерты

Producer:
Outbox в той же транзакции

Брокер:
durable, подтверждения,
репликация

Consumer:
идемпотентность (Inbox),
ретраи, DLQ

Наблюдаемость:
traceparent в заголовках,
lag, DLQ-алерты

Цепочка надёжной доставки

Проблемы, с которыми придётся жить:

  • At-least-once доставка → дубли → идемпотентные обработчики.
  • Порядок не гарантирован глобально → партиционирование по ключу агрегата, версии в событиях, проверка «событие старее текущего состояния».
  • Eventual consistency → UI и процессы должны учитывать задержку.
  • Ядовитые сообщения → ретраи с ограничением и Dead Letter Queue.

Контракты событий

Событие как публичный контракт
namespace Shop.Orders.Contracts.V1;

/// <summary>Опубликовано после успешного оформления заказа.</summary>
public sealed record OrderPlaced(
    Guid EventId,               // идентификатор для идемпотентности
    Guid OrderId,
    Guid CustomerId,
    decimal Total,
    string Currency,
    DateTimeOffset OccurredAt,
    int SchemaVersion = 1);

Правила эволюции:

  • Добавлять поля — безопасно (потребители игнорируют неизвестное); удалять и переименовывать — нет.
  • Несовместимые изменения — новый тип/версия (OrderPlacedV2), публиковать обе версии на период миграции.
  • Реестр схем (Schema Registry для Avro/Protobuf в Kafka) или контрактные тесты (Pact).
  • CloudEvents — стандартный конверт метаданных (id, source, type, time, datacontenttype).

Когда EDA уместна

Хорошо Плохо
Много независимых реакций на одно событие Нужен немедленный ответ с результатом (синхронный запрос)
Интеграция сервисов разных команд Строгая согласованность между шагами
Сглаживание пиков нагрузки Простое приложение без интеграций
Аудит, аналитика, потоковая обработка Команда без опыта наблюдаемости и работы с брокером

Вопросы на засыпку

Что включать в событие: только ID или все данные?

Компромисс. Тонкие события (notification) проще версионировать, но заставляют потребителей обращаться к источнику. Толстые (state transfer) дают автономность, но раздувают контракт. Часто выбирают «достаточные» данные: то, что нужно большинству потребителей, + ID для остального.

Как отлаживать систему на событиях?

Сквозная трассировка (OpenTelemetry с передачей traceparent в заголовках сообщений), correlation id в логах, визуализация потоков (Jaeger/Tempo), мониторинг lag потребителей и DLQ, возможность повторно проиграть сообщения из DLQ после исправления.

Чем событие отличается от сообщения?

Сообщение — транспортная единица (конверт с заголовками и телом). Событие — один из видов содержимого сообщения (наряду с командами и запросами/ответами). Брокер доставляет сообщения; семантика «событие/команда» — договорённость приложения.

Что такое event storming?

Практика совместного моделирования с бизнесом: на стене раскладываются доменные события (оранжевые стикеры) в хронологическом порядке, затем команды, акторы, агрегаты, политики. Помогает найти границы контекстов и потоки событий до написания кода.

Итог

Event-Driven Architecture связывает компоненты через факты о произошедшем: производитель не знает потребителей, новые реакции добавляются без изменения источника, пики сглаживаются брокером. Плата — eventual consistency, дубли, порядок и сложность наблюдения; поэтому EDA требует outbox, идемпотентных потребителей, DLQ, версионирования контрактов и хорошей трассировки.