GH GambleHub

DDD в iGaming-ядрі

iGaming-платформа - це складна доменна система на стику фінансів, розваг і комплаєнсу. DDD допомагає утримувати складність: виділяє bounded contexts, фіксує ubiquitous language, захищає інваріанти агрегатами, спрощує інтеграції через антикорупційні шари і робить поведінку системи прозорою завдяки доменним подіям.

1) Карта доменів і bounded contexts (стратегічний дизайн)

Рекомендована декомпозиція:
  • Player/KYC Context - реєстрація, верифікація, ліміти відповідальної гри, статуси KYC/AML.
  • Wallet/Ledger Context - баланси, резервування, проводки, мультивалютність, курси.
  • Betting Context - ставки/тікети, пара/результати, котирування, розрахунок (settlement), скасування.
  • Casino/Game Round Context - сесії, раунди, спини, RTP-контроль, ліміти ставок.
  • Bonus/Promo Context - правила бонусів, вагер, еквайринги бонусних коштів, анти-аб'юз.
  • Risk/Fraud Context - скоринг, поведінкові сигнали, тригери блокувань/тайм-аутів.
  • Payments Context - депозити/висновки, статуси платіжних шлюзів, chargeback-події.
  • Compliance/Reporting Context - звіти регуляторам, санкційні списки, аудит.
  • Content/Provider Integration Context - інтеграція з провайдерами ігор, каталоги, тех. та ін. статуси.
  • Analytics/Read Models - проекції та вітрини під продуктові читання.
💡 Міжконтекстні зв'язки - через доменні події та асинхронні команди; синхронні виклики - тільки там, де це дійсно контракт домену і потрібна сувора узгодженість.

2) Ubiquitous language: ядро термінів

Player (Гравець), Session (Сесія), GameRound (Раунд), Bet/Ticket (Ставка),

Ledger Entry (Проводка), Hold/Reserve (Резерв), Settlement (Розрахунок),

Bonus Credit / Bonus Balance, Wagering Requirement (Вейджер),

KYC Tier, Limit (депозит/сесія/втрати), Self-Exclusion,

Provider Game, RTP Window, Risk Flag, Compliance Case.

Ці назви однаково вживаються в коді, БД, документації, тестах та інтерфейсах.

3) Агрегати та інваріанти (тактичний дизайн)

3. 1 Wallet (Aggregate: `Wallet`)

Інваріанти:
  • Баланс не йде в мінус.
  • Резерв + доступний ≤ загальний баланс.
  • Проводка атомарна і ідемпотентна (по'operation _ id').
Команди/події:
  • `Wallet. Reserve(amount, reason, op_id)` → `WalletReserved`
  • `Wallet. Commit(op_id)` → `WalletCommitted`
  • `Wallet. Rollback(op_id)` → `WalletRolledBack`

Межа: Wallet не знає про Bet/Bonus; він обслуговує операції проводок і резервів.

3. 2 Bet/Ticket (Aggregate: `Bet`)

Інваріанти:
  • Ставка може бути прийнята тільки в активному вікні котирувань; сума ≤ ліміту гравця/сесії.
  • Після'Settled'статус «фіналізований»; повторний розрахунок допускається тільки через компенсуючі операції (void/recalc) з чітким аудитом.
Команди/події:
  • `Bet. Place(player_id, amount, price, op_id)` → `BetPlaced` (требует Wallet. Reserve)
  • `Bet. Settle (outcome, payout)'→'BetSettled'( вимагає Wallet. Commit/Release)
  • `Bet. Void(reason)` → `BetVoided`

Межа: Bet не «лізе» в Wallet - звертається через доменні команди/оркестрацію.

3. 3 GameRound (Aggregate: `Round`)

Інваріанти:
  • Кожен спін/раунд має унікальний'round _ id'і пов'язану суму ставки/виграшу.
  • RTP-вікно не перевищує задані пороги (на рівні провайдера + локальні правила).
Події:
  • `Round. Started`, `Round. Staked`, `Round. Resulted`, `Round. Closed`.

3. 4 Bonus (Aggregate: `BonusGrant`)

Інваріанти:
  • Вейджер зменшується тільки від валідного обороту, списання бонусу не йдуть в дебет.
  • Неможливо одночасно списати бонус і реальні кошти не за правилом пріоритету.
Події:
  • `BonusGranted`, `BonusWagered`, `BonusExpired`, `BonusConverted`.

4) Оркестрації, саги та узгодженість

Синхронно (CP): прийом ставки та резерв коштів - єдиний шлях: `Bet. Place` → `Wallet. Reserve'( через доменну команду/оркестратор з дедлайном).
Асинхронно (EC): розрахунок ставки, нарахування бонусів, аналітика - через події + outbox.
TCC-варіант: «TryReserve» (hold), «Confirm» (commit), «Cancel» (rollback) для грошових ефектів.
Ідемпотентність: всі команди несуть'operation _ id', консьюмери -'inbox'.

5) Антикорупційні верстви (ACL) та інтеграції

Provider ACL: трансляція провайдерських подій'SpinResult','BonusWin'у внутрішні'Round. Resulted`, `BonusWagered`. Схеми та версії - всередині ACL.
PSP ACL: нормалізація статусів платежів, ідемпотентність по'psp _ tx _ id', переказ в'LedgerEntry'.
Compliance ACL: інтеграції зі списками санкцій/РЕР - у зовнішньому контексті; всередину домену потрапляють тільки нормалізовані «ScreeningUpdated».

Правило: зовнішні словники/формати не «просочуються» всередину ядра.

6) Проекції та Read Models

Player Profile Read Model: статуси KYC, ліміти, активні бонуси, свіжі транзакції.
Balances Read Model: швидкі читання для UI/маркетингу; джерело - «Wallet» події.
Bet History Read Model: пагінація по датах/іграх; джерело -'BetPlaced/Settled'.
Compliance Reports: матеріалізовані уявлення по тенанту/регіону.

Всі проекції - ідемпотентні upsert'и з версіонуванням і'as _ of/freshness'.

7) Мульти-тенант і мульти-регіон

Всі ключові сутності несуть'tenant _ id'і (при необхідності)'region'.
Межі даних: гравець, гаманець, ставки - «домашній» регіон; крос-регіональні тільки агрегати/звіти.
Fairness/квоти: ліміти на команди/сек і редрайви за тенантами.
Residency/комплаєнс: персональні дані і проводки не залишають регіон.

8) Вибір узгодженості (PACELC) за контекстами

Wallet/Ledger — Strong/CP: лінійризовані проводки, кворум записів.
Bet acceptance - синхронне підтвердження (CP) + швидкі Read Models для UI.
Settlement/Bonus/Analytics - EC з детермінованим merge/компенсаціями.
KYC/Compliance - може бути EC для статусів, але «блокуючі» правила застосовуються синхронно.

9) Доменні події: контракти та версія

Мінімальний набір полів:
json
{
"event_id": "uuid",
"event_type": "BetPlaced",
"occurred_at": "timestamp",
"tenant_id": "T123",
"aggregate_id": "BET-...-UUID",
"version": 7,
"payload": { "...domain fields..." },
"schema_version": "v3"
}
Правила:
  • Back/forward-compat схем; еволюція через'schema _ version'.
  • 'outbox'в транзакції з доменними змінами; публікація батчами з backoff.

10) Приклад потоку «Ставка з бонусом» (словесна послідовність)

1. `Bet. Place'( команда) → перевірка лімітів гравця і правил бонусу →'Wallet. Reserve(real+bonus_equiv, op_id)`

2.'BetPlaced'( подія) → Read Model оновлює «Відкриті ставки»

3. Провайдер публікує результат → ACL →'Round. Resulted`

4. Оркестратор розраховує: `Bet. Settle(outcome,payout)` → `Wallet. Commit (op_id)'і, при виграші,'BonusWagered'→ можлива конверсія бонусу в реальні.
5.'BetSettled'→ проекції історії та балансів, звітність.

11) Інваріанти та політика тестування

Ключові інваріанти:
  • Сума всіх'LedgerEntry'по гаманцю дорівнює балансу; немає негативних залишків.
  • Не можна прийняти ставку при активному self-exclusion/замороженому KYC-статусі.
  • Вейджер може тільки зменшуватися і не гойдатися «в мінус».
  • Settlement не змінює статус вже фіналізованої ставки - тільки через'Void/Recalc'+ компенсуюча проводка.
Тестування:
  • Property-based тести інваріантів гаманця і ставок.
  • Контури хаосу: затримки провайдера, відмови PSP, редрайви outbox/DLQ.
  • Контроль схем: міграції подій, backfill проекцій.

12) Телеметрія і аудит

Метрики: p95/p99 на PlaceBet/Reserve/Commit, частка відмов по лімітах/КУС, DLQ rate, redrive success, lag проекцій.
Трейсинг: спани «komanda→agregat→outbox→konsyumer→proyektsiya», теги'tenant _ id','operation _ id','saga _ id'.
Аудит: незмінний журнал доменних дій, порівнянний з регуляторними вимогами.

13) Схема зберігання (спрощено)

Wallet:

wallet(id, tenant_id, currency, balance, reserved, version)
ledger(id, wallet_id, amount, type, operation_id, occurred_at)
holds(id, wallet_id, amount, operation_id, expires_at, status)
Bet:

bet(id, tenant_id, player_id, amount, price, status, placed_at, settled_at, operation_id)
Bonus:

bonus_grant(id, tenant_id, player_id, amount, wager_left, status, expires_at)

Версіонування на агрегатах ('version') захистить від lost update при конкурентному записі.

14) Приклад API команд (псевдо)

http
POST /bets. place
{
"tenant_id":"T1",
"player_id":"P42",
"amount":"10. 00",
"price":"2. 1",
"operation_id":"op-uuid",
"context":{"game_id":"g777","channel":"web"}
}
→ 202 Accepted + BetPlaced

POST /wallets. reserve
{ "wallet_id":"W1", "amount":"10. 00", "operation_id":"op-uuid", "reason":"bet" }
→ 200 { "reserved_balance":"..." }

Всі команди - з'operation _ id'для ідемпотентності, відповіді - з'as _ of '/' version'.

15) Безпека та відповідність

RLS/ACL: всі запити - в контексті'tenant _ id', доступ за ролями.
PII-мінімізація: відділення доменних подій від персональних даних; маскування в DLQ/логах.
Регуляторні звіти: проекції з незмінними хеш-підписами за вікнами часу.

16) Типові помилки

Сильна зв'язність між контекстами (Wallet безпосередньо знає Bet/Bonus).
Dual-write в різні контексти без саг/outbox → неузгодження балансів і статусів.
Відсутність ідемпотентності команд і консьюмерів → дублі проводок/розрахунків.
Протікання провайдерських контрактів в доменну модель (складніше мігрувати).
Один «гігантський» агрегат (Player включає всі) → блокування, низький throughput.
Немає явних інваріантів - їх неможливо перевірити і моніторити.

17) Швидкі рецепти

Старт: зафіксуйте Ubiquitous Language і контекстні межі; документуйте інваріанти.
Гроші: Wallet/Ledger - CP, кворумні записи, TCC для зовнішніх ефектів.
Ставки: синхронний прийом + асинхронний розрахунок, все через події і outbox; ідемпотентність скрізь.
Бонуси: окремий агрегат з чітким пріоритетом списань і вейджером.
Інтеграції: завжди через ACL + схеми/версії; ніяких «сировинних» payload'ів в ядрі.
Читання: проекції/вітрини на потреби продукту; SLA свіжості +'as _ of'.
Оперування: метрики інваріантів, DLQ/редрайв плейбуки, rebuild вітрин.

18) Чек-лист перед продом

  • Визначені bounded contexts і їх контракти (команди/події).
  • Агрегати мають явні інваріанти, версіонування та ідемпотентні команди.
  • Грошові операції - через ТСС/сувору транзакційність; аудит включено.
  • Інтеграції - через ACL з версіонуванням схем і тестами еволюції.
  • Впроваджені outbox/inbox, DLQ і безпечний редрайв.
  • Проекції реалізують SLA свіжості, є метрики lag/staleness.
  • Мульти-тенантні квоти/ліміти і data residency дотримані.
  • Спостережуваність: трейсинг «komanda→sobytiye→proyektsiya», алерти за інваріантами.
  • Документація: мова домену, діаграми контекстів, плейбуки інцидентів.

Висновок

DDD в iGaming-ядрі - це дисципліна поділу складності: чіткі межі контекстів, агрегати з інваріантами, події як джерело правди, ACL для зовнішніх інтеграцій і усвідомлений вибір узгодженості. Такий підхід робить платформу масштабованою, надійною і відповідною регуляціям, прискорює розробку фіч і знижує операційні ризики - навіть при швидкому зростанні трафіку, географій і продуктової лінійки.

Contact

Зв’яжіться з нами

Звертайтеся з будь-яких питань або за підтримкою.Ми завжди готові допомогти!

Telegram
@Gamble_GC
Розпочати інтеграцію

Email — обов’язковий. Telegram або WhatsApp — за бажанням.

Ваше ім’я необов’язково
Email необов’язково
Тема необов’язково
Повідомлення необов’язково
Telegram необов’язково
@
Якщо ви вкажете Telegram — ми відповімо й там, додатково до Email.
WhatsApp необов’язково
Формат: +код країни та номер (наприклад, +380XXXXXXXXX).

Натискаючи кнопку, ви погоджуєтесь на обробку даних.