GH GambleHub

Event-Driven ядро

Что такое Event-Driven ядро

Event-Driven ядро (EDC) — это «позвоночник» архитектуры, в котором бизнес-факты фиксируются и распространяются как неизменяемые события, а остальная функциональность (чтение, интеграции, аналитика, кэши, нотификации) строится поверх потока этих событий. Ядро задает контракт событий, правила доставки и инварианты порядка/идемпотентности, обеспечивая слабую связность и масштабируемость.

Ключевая идея: сначала записать факт (ядро), а затем независимо обогащать и проецировать его в нужные модели. Это уменьшает связность и повышает устойчивость к частичным сбоям.

Цели и свойства EDC

Истинность фактов: каждое событие — неизменяемая запись «что произошло».
Слабая связность: продюсеры не знают потребителей; расширение системы — добавлением подписчиков.
Масштабирование: горизонтальный рост по партициям/топикам, независимые потребители.
Наблюдаемость и аудит: сквозные идентификаторы, воспроизводимость, ретенции и репроигрывание.
Управляемая эволюция: версии схем, совместимость, deprecation.

Архитектурные компоненты

1. Шина/брокер событий: Kafka/NATS/Pulsar/SNS+SQS — каналы, партиции, ретенции.
2. Реестр схем: JSON Schema/Avro/Protobuf для совместимости и эволюции.
3. Outbox/CDC-контур: атомарная фиксация факта + публикация без «двойной записи».
4. Проекции/чтения (CQRS): материализованные представления для быстрых запросов.
5. Саги/оркестрация: координация долгоживущих процессов через события/команды.
6. Обогащение: отдельные топики `.enriched`/`.derived` без влияния на критический путь.
7. Обсервабилити: трассировка, логирование, метрики по событиям и лагам.

Модель событий

Типы событий

Domain Events: бизнес-факты (`payment.authorized`, `kyc.approved`).
Integration Events: ориентированы на внешние системы (стабильные, медленно меняются).
Change Data Capture (CDC): технические изменения записи (используйте для миграций/интеграций).
Audit/Telemetry: действия акторов, безопасность, SLA.

Обязательные атрибуты (ядро)

json
{
"event_id": "uuid",
"event_type": "payment. authorized. v1",
"occurred_at": "2025-10-31T11:34:52Z",
"producer": "payments-service",
"subject": { "type": "payment", "id": "pay_123" },
"payload": { "amount": 1000, "currency": "EUR", "method": "card" },
"schema_version": 1,
"trace_id": "abc123",
"partition_key": "pay_123"
}

Рекомендации: `event_id` глобально уникален, `partition_key` задает порядок для сущности, `trace_id` обеспечивает корреляцию.

Семантика доставки и идемпотентность

At-least-once (по умолчанию у многих брокеров): потребители обязаны быть идемпотентными.
At-most-once: приемлемо лишь для второстепенных телеметрий.
Exactly-once: достигается на уровне потока и хранилища через транзакции/идемпотентные ключи/лейки (дороже, нужна веская причина).

Шаблон идемпотентности потребителя

Дедуп-таблица по `event_id`/`(event_id, consumer_id)` с TTL ≥ ретенции топика.
Upsert вместо insert; версионирование проекций по `sequence`/`occurred_at`.
Операции в рамках транзакции: отметка «видел» + изменение состояния.

Порядок и партиционирование

Гарантированный порядок в пределах партиции.
Выбирайте `partition_key` так, чтобы все события одной агрегат-сущности попадали в одну партицию (`user_id`, `payment_id`).
Избегайте «горячих ключей»: хеш с солью/под-ключи, если требуется распределить нагрузку.

Схемы и эволюция

Additive-first: новые опциональные поля, запрет на смену типов/семантики без major-версии.
Совместимость: BACKWARD/FORWARD в реестре схем; CI блокирует несовместимые изменения.
Именование: `domain.action.v{major}` (`payment.authorized.v1`).
Миграции: публикуйте пары `v1` и `v2` параллельно, обеспечьте двойное излучение (dual-write через outbox), снимайте `v1` после перехода.

Outbox и CDC

Outbox (рекомендуется для транзакционных сервисов)

1. В одной БД-транзакции: сохраняем доменную запись и событие в outbox.
2. Фоновый паблишер читает outbox, публикует в брокер, помечает «отправлено».
3. Гарантии: нет «потери» факта при падениях, нет рассинхронизации.

CDC (Change Data Capture)

Подходит для существующих систем/миграций; источник — лог репликации БД.
Требует фильтрации/перекодирования в доменные события (не транслируйте «сырые» таблицы вовне).

CQRS и проекции

Команды изменяют состояние (часто синхронно), события — формируют проекции (асинхронно).
Проекции рассчитаны на запросы (поиск, списки, отчеты), обновляются подписчиками.
Временная рассинхронизация — норма: показывайте устойчивый UX («данные обновятся через несколько секунд»).

Саги: координация процессов

Оркестрация: один координатор посылает команды и ждет событий.
Хореография: участники реагируют на события друг друга (проще, но требует дисциплины в контрактах).
Правила: четкие компенсации и тайм-ауты, повторяемые шаги, идемпотентные обработчики.

Наблюдаемость

Trace/Span: прокидывайте `trace_id`/`span_id` через заголовки при порождении событий.
Метрики: лаг потребителей, скорость публикации/потребления, dead-letter rate, доля дедупликаций.
DLQ/parking lot: неуспешные сообщения — в отдельный топик с алертом; обеспечьте переобработку.

Безопасность и соответствие

Классификация данных: ядро содержит только необходимый минимум PII/финданных (модель обратной пирамиды), детали — в обогащениях.
Подпись/хэш критичных атрибутов, контроль целостности.
Шифрование in-flight и at-rest, секционирование прав по темам/консюмерам (IAM/ACL).
Политики ретенции и права на забвение: четко определены для каждого топика.

Производительность и устойчивость

Backpressure: у потребителей — ограничение конкурентности, у брокера — квоты/лимиты.
Batch-обработка и компрессия: группируйте записи для снижения накладных расходов.
Ретраи с джиттером и DLQ вместо бесконечных попыток.
Rebalance-стойкость: храните оффсеты транзакционно/внешне, ускоряйте холодный старт снапшотами.

Типовые шаблоны событий

Ядро платежей

`payment.initiated.v1` → `payment.authorized.v1` → `payment.captured.v1` → `payment.settled.v1`

Отказы: `payment.declined.v1`, `payment.refunded.v1`

Партиционирование: `payment_id`

SLA: лаг ядра ≤ 2с p95; идемпотентность потребителей обязательна.

KYC/верификации

`kyc.started.v1` → `kyc.document.received.v1` → `kyc.approved.v1`/`kyc.rejected.v1`

PII — минимально; детали документа — в `kyc.enriched.v1` с ограниченным доступом.

Аудит/безопасность

`audit.recorded.v1` с атрибутами `actor`, `subject`, `action`, `occurred_at`, `trace_id`.
Непрерывная ретенция/архивирование; повышенная целостность (WORM-хранилища).

Антипаттерны

Fat Event: перегруженные payload’ы без нужды, утечки PII.
Hidden RPC через события: ожидание синхронных ответов «здесь и сейчас».
Сырые CDC наружу: тесная связность со схемой БД.
Нет идемпотентности у потребителей: дубли приводят к двойным побочным эффектам.
Один общий топик «на все»: конфликт интересов, проблемный порядок, сложная эволюция.

Пошаговое внедрение EDC

1. Картирование домена: выделите ключевые агрегаты и жизненные циклы.
2. Каталог событий: названия, смыслы, инварианты, обязательные поля.
3. Схемы и реестр: выберите формат, включите правила совместимости.
4. Outbox/CDC: для каждого продюсера определите механизм публикации фактов.
5. Партиционирование: выберите ключи и оцените горячие ключи/переразбиение.
6. Идемпотентность: шаблон дедупа + транзакционность потребителей.
7. Проекции: определите материализованные модели и SLA обновления.
8. Обсервабилити: трассировка, лаги, DLQ, алерты.
9. Security/PII: классификация данных, шифрование, ACL.
10. Гайд по эволюции: политика версий, депрекейт, dual-write для миграций.

Чек-лист продакшена

  • У каждого события есть `event_id`, `trace_id`, `occurred_at`, `partition_key`.
  • Схемы в реестре, включены проверки совместимости.
  • Идемпотентность потребителей реализована и протестирована.
  • Настроены DLQ/parking lot и алерты на ошибки публикации/потребления.
  • Проекции пересоздаются из лога (replay) с приемлемым временем.
  • Ограничен доступ к PII; минимальные payload’ы в ядре.
  • SLA по лагам/доставке замеряются и видны на дашбордах.
  • Есть план миграции версий событий и окон депрекейта.

FAQ

Чем EDC отличается от «просто шины»?
Ядро — это не только брокер, но и контракт событий, правила порядка/идемпотентности, процессы эволюции и наблюдаемость.

Можно ли строить только на CDC?
CDC подходит для интеграций/миграций, но доменные события яснее выражают смысл и стабильнее переживают изменения БД.

Как быть с согласованностью?
Принимаем eventual consistency и проектируем UX/процессы под нее (индикаторы обновления, ретраи, компенсации).

Когда нужен exactly-once?
Редко: когда удвоение строго недопустимо и невозможно компенсировать. Чаще достаточно at-least-once + идемпотентность.

Итог

Event-Driven ядро превращает поток бизнес-фактов в надежный фундамент системы. Четкие контракты событий, дисциплина доставки и наблюдаемость дают масштабируемость, устойчивость и скорость эволюции — без хрупких синхронных связей и «шторма» регрессий при изменениях.

Contact

Свяжитесь с нами

Обращайтесь по любым вопросам или за поддержкой.Мы всегда готовы помочь!

Начать интеграцию

Email — обязателен. Telegram или WhatsApp — по желанию.

Ваше имя необязательно
Email необязательно
Тема необязательно
Сообщение необязательно
Telegram необязательно
@
Если укажете Telegram — мы ответим и там, в дополнение к Email.
WhatsApp необязательно
Формат: +код страны и номер (например, +380XXXXXXXXX).

Нажимая кнопку, вы соглашаетесь на обработку данных.