Перейти к содержанию

Event-Driven Architecture

Раздел 01 · Уровни и общие гарантии

Тип материала: архитектурный разбор. Схемы и примеры иллюстративные, если не указано иное.

Уровень: межкомпонентное взаимодействие. Проверка источников: 2026-11-02.

1. Назначение и решаемая проблема

EDA уменьшает временную связанность producer и downstream consumers: факт принятой операции публикуется, а несколько систем реагируют независимо. Подходит для projections, notifications, risk features и integration.

Асинхронность переносит часть сложности в contracts и operations. Если успешный API response зависит от решения fraud service, эту зависимость нельзя скрыть словом «event-driven»: нужен явный synchronous decision или durable pending workflow.

2. Принцип работы

Producer фиксирует бизнес-изменение и публикует событие о состоявшемся факте. Broker/log доставляет его подписчикам. Каждый consumer владеет своим state и правилами повторной обработки.

Отличать event «PaymentCaptured» от command «CapturePayment». У command есть ответственный исполнитель; event может иметь много независимых потребителей. Event-carried state transfer содержит достаточно данных для проекции; notification event только сообщает, что нужно запросить детали. Второй вариант сохраняет runtime dependency на source API.

3. Архитектурная схема

flowchart TB
    C["Command API"] --> D["Authoritative DB и outbox"]
    D --> R["Relay"]
    R --> L["Event log"]
    L --> P["Projection consumer"]
    L --> F["Risk feature consumer"]
    L --> N["Notification consumer"]
    P --> V["Read model"]
    F --> S["Feature store"]
    N --> X["External provider"]

4. Walkthrough движения данных

  1. API принимает idempotent command. В локальной транзакции записываются business state и outbox event; схема использует Outbox как выбранную технику, EDA сама его не требует.
  2. Relay публикует event_id, entity_id, revision, occurred_at, schema_version и correlation metadata.
  3. Projection consumer обновляет read model; feature consumer обновляет признаки; их progress независим.
  4. Notification consumer вызывает внешний сервис со stable idempotency key. При неизвестном результате повторного вызова применяется provider contract, а не уверенность в broker.
  5. Некорректный payload направляется в контролируемый quarantine/DLQ; owner исправляет причину и планирует replay.
  6. API чтения сообщает projection revision/freshness, чтобы клиент понимал, что write уже принят, но view может ещё отставать.

5. Технологии реализации

Kafka/Pulsar подходят для replayable log; RabbitMQ — для маршрутизации queue workloads; cloud event buses — другой operational trade-off. Выбор должен учитывать retention, fan-out, routing и consumer recovery.

Schema Registry контролирует структурную compatibility, но изменение «amount теперь включает fee» требует семантического contract/versioning. Outbox/CDC, consumer inbox, tracing и catalog ownership — отдельные механизмы реализации.

6. Консистентность и гарантии обработки

Обычно проектируют durable at-least-once delivery с idempotent consumers; конкретная гарантия зависит от producer commit, broker durability и sink. Порядок чаще локален одному partition/key. Между topics или retry topic нет общего business order.

Read models eventual при выполнении условий durable delivery, retry и корректной обработки. Broker transactions не объединяют произвольную OLTP-транзакцию и внешний сервис. Eventual не означает, что рассогласование исчезнет без retries и reconciliation.

7. Отказы и восстановление

Сбой Recovery Что не гарантируется автоматически
Producer commit выполнен, publish сорвался Relay читает outbox повторно Без outbox событие могло бы пропасть
Consumer сделал effect до crash Inbox/idempotency и redelivery Commit offset не отменяет предыдущий effect
Schema семантически изменилась Версионировать contract, shadow consumer и backfill Registry не понимает бизнес-смысл
Downstream остановлен дольше retention Restore из archive/source snapshot Offset reset не возвращает удалённый log

Backpressure должен замедлять безопасный участок, а не создавать бесконечные retries в общий topic. DLQ без owner и replay procedure — место накопления ошибок, не recovery strategy.

8. Масштабирование и узкие места

Partition по entity_id сохраняет локальный порядок, но hot entity ограничивает throughput. Разделять work по независимым keys можно, пока не нарушаются invariants. Увеличение partitions требует проверить key mapping и state migration.

Fan-out увеличивает общее число downstream writes. Producer может быть здоров при растущем consumer lag, поэтому metrics нужны по каждому critical consumer и по oldest unprocessed event.

9. Преимущества и недостатки

Плюсы: независимое масштабирование consumers, новые projections без изменения producer, буферизация сбоев. Минусы: delayed visibility, contract management, tracing через async hops и сложные replay rules.

EDA полезна там, где business process допускает pending state. Для короткой операции в одной БД request/response и локальная транзакция часто проще.

10. Альтернативные решения

CQRS выделяет write/read models; Event Sourcing меняет authoritative persistence; EDA не требует ни того, ни другого. Saga координирует multi-step process поверх events/commands. Синхронный API лучше для немедленного ответа с единственным owner, batch transfer — для допустимой задержки.

11. Реальные примеры и границы подтверждения

FACT: Betfair описывает переход market-data read services от polling Oracle к Kafka streaming. Это industrial example event-driven data distribution; из него не следуют guarantees wallet или payout. Betfair, публикационная строка 06-09-2018.

INFERENCE: read integration — хороший кандидат для decoupling, поскольку потребители могут независимо строить формы чтения. Называть весь Exchange Event Sourcing только по этому источнику нельзя.

12. Применение в FinTech и iGaming

FinTech: опубликованный capture event обновляет reporting и risk features; окончательное подтверждение списания определяется payment/ledger workflow.

iGaming: odds/market events распространяются в read models, а bet acceptance сверяет version/market state у authoritative owner. Отдельный поток responsible gaming signals не должен означать, что критический запрет гарантированно применяется лишь eventual consumer.

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

  • Публиковать mutable database row как стабильный public contract без owner.
  • Делать HTTP callback из consumer и считать его exactly-once из-за broker settings.
  • Обещать global order по нескольким partitions.
  • Перепубликовывать исторические события в канал live interventions.
  • Принимать рост lag за нормальную eventual consistency без предела freshness.

14. Вопросы технического интервью с ответами

EDA требует Kafka? Нет, это способ взаимодействия; выбор transport определяется replay/routing/retention и guarantees.

Где проверять баланс? В authoritative transaction boundary, а не в отстающей аналитической projection.

Что должен содержать event contract? Стабильную identity, semantics, schema/version, ordering scope и правила correction; correlation ID полезен для tracing, но не заменяет event_id.

15. Практическое упражнение

HYPOTHETICAL: один PaymentCaptured должен обновить историю и отправить receipt. Consumer падает после внешней отправки и до offset commit.

Опишите durable idempotency contract и replay mode, который перестраивает историю без повторной отправки receipt. Ожидаемый результат: один business notification effect, восстановленная projection и видимый lag; без поддержки dedup у внешнего сервиса явно назовите остающееся UNKNOWN.

Дополнительные источники

Kafka 4.1 design; Betfair; Outbox.