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 движения данных¶
- API принимает idempotent command. В локальной транзакции записываются business state и outbox event; схема использует Outbox как выбранную технику, EDA сама его не требует.
- Relay публикует event_id, entity_id, revision, occurred_at, schema_version и correlation metadata.
- Projection consumer обновляет read model; feature consumer обновляет признаки; их progress независим.
- Notification consumer вызывает внешний сервис со stable idempotency key. При неизвестном результате повторного вызова применяется provider contract, а не уверенность в broker.
- Некорректный payload направляется в контролируемый quarantine/DLQ; owner исправляет причину и планирует replay.
- 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.