iGaming內核中的DDD
iGaming平臺是一個復雜的域系統,位於金融,娛樂和合規的交界處。DDD有助於保持復雜性:突出邊界引用,捕獲ubiquitous語言,通過聚合保護不變量,通過反腐敗層簡化集成,並通過域事件使系統行為透明。
1)域名映射和邊界映射(戰略設計)
建議的解構:- Player/KYC Context-註冊、驗證、負責任的遊戲限制、KYC/AML狀態。
- Wallet/Ledger Context-資產負債表,備份,布線,多種貨幣性,課程。
- Betting Context-投註/滴答作響,成對/結果,報價,計算(定位),取消。
- Casino/Game Round Context-會話、回合、旋轉、RTP控制、投註限制。
- Bonus/Promo Context-獎金,旅行車,獎金收購和反抽獎規則。
- Risk/Fraud Context-得分,行為提示,鎖定/超時觸發器。
- Payments Context-存款/結算、支付網關狀態、充電站事件。
- Compliance/Reporting Context-向監管機構報告、制裁名單、審計。
- Content/Provider Integration Context-與遊戲提供商、目錄、技術集成。狀態。
- Analytics/Read Models-產品閱讀的投影和展示。
2)Ubiquitous語言: 術語的核心
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.
這些名稱在代碼,DB,文檔,測試和接口中同樣使用。
3)聚合和不變量(戰術設計)
3.1 Wallet (Aggregate: `Wallet`)
不變量:- 平衡不會消失。
- 備份+可用≤總余額。
- Atomarn和Idempotent接線(通過「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`)
不變量:- 只有在活動報價窗口中才能接受投註;玩家/會話上限≤金額。
- 在「定居」之後,狀態被「最終化」;僅通過具有明確審核的補償性操作(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`)
不變量:- Vager僅從有效周轉中減少,獎金註銷不會進入借記。
- 根據優先權規則,不可能同時註銷獎金和實際資金。
- `BonusGranted`, `BonusWagered`, `BonusExpired`, `BonusConverted`.
4)編排,傳奇和連貫性
同步(CP):接受利率和資金儲備-單一途徑:'Bet。Place` → `Wallet.Reserve'(通過帶截止日期的域命令/編曲器)。
異步(EC):通過事件+outbox計算利率、累積獎金、分析。
TCC變體:「TryReserve」(撲克),「Confirm」(commit),「Cancel」(rollback)用於貨幣效果。
相同性:所有團隊都攜帶「operation_id」,用戶攜帶「inbox」。
5)反腐敗層(ACL)和集成
提供者ACL:將提供商「SpinResult」,「BonusWin」事件廣播到內部的「Round」。Resulted`, `BonusWagered`.模式和版本-在ACL中。
PSP ACL:正常化支付狀態,通過「psp_tx_id」的冪等,翻譯為「LedgerEntry」。
法規遵從性ACL:在外部環境中與制裁/RER列表集成;僅歸一化的「ScreeningUpdated」進入域內。
規則:外部字典/格式不會「滲入」內核。
6)投影和閱讀模型
播放器配置文件閱讀模型:KYC狀態、限制、活動獎金、新鮮交易。
Balances閱讀模型:UI/市場營銷的快速閱讀;來源-「Wallet」事件。
Bet History閱讀模型:按日期/遊戲劃分;來源-「BetPlaced/Settled」。
Compliance Reports: Tenant/Region的實例化視圖。
所有投影都是具有轉換和「as_of/freshness」的等效的upsert's。
7)多特南特和多區域
所有關鍵實體都帶有「tenant_id」和(如果需要)「區域」。
數據邊界:玩家,錢包,賭註是「家庭」區域;僅跨區域匯總/報告。
公平性/配額:tenant的命令/秒限制和重復。
住宅/合規性:個人數據和布線不會離開該地區。
8)按上下文選擇一致性(PACELC)
Wallet/Ledger-Strong/CP:線性布線,法定記錄。
Bet acceptance-用於UI的同步確認(CP)+快速閱讀模型。
定位/獎金/分析-具有確定性merge/補償的 EC。
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"
}
規則:
- 背部/前向復合電路;通過「schema_version」演變。
- 域更改事務中的「outbox」;batchami與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」的總和等於資產負債表;沒有負余額。
- 在主動自我釋放/凍結的KYC狀態下不能接受出價。
- Vager只能減小並且不會「減去」擺動。
- Settlement不會更改已經最終的投註狀態-僅通過「Void/Recalc」+補償布線。
- 基於財產的錢包和賭註不變性測試。
- 混沌輪廓:提供商延遲、PSP故障、outbox/DLQ重新分區。
- 模式控制:事件遷移,後置投影。
12)遙測和審計
指標:PlaceBet/Reserve/Commit上的p95/p99,限值/CUS故障率,DLQ利率,redrive成功,lag投影。
Tracing: spans 「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)
在單元上進行轉換(「版本」)將防止在競爭性記錄下丟失更新。
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/Logs中的偽裝。
監管報告:在時間窗口中具有不可變哈希簽名的投影。
16)典型錯誤
上下文之間的緊密連接(Wallet直接知道Bet/Bonus)。
在沒有傳奇/outbox的不同情況下進行雙重寫作→資產負債表和狀態的不一致。
團隊和用戶缺乏相等性→電線/計算雙倍。
提供商合同流向域模型(更難遷移)。
一個「巨型」單元(Player包括所有)鎖定→,低通量。
沒有顯式不變式-無法驗證和監視它們。
17)快速食譜
開始:記錄優美的語言和上下文界限;記錄不變量。
金錢:錢包/Ledger-CP,法定記錄,TCC用於外部效果。
投註:同步接收+異步計算,全部通過事件和outbox;等效性無處不在。
獎金:一個單獨的單元,具有明確的註銷優先級和vager。
集成:始終通過ACL+模式/版本;核心沒有「原材料」付費。
閱讀:針對產品需求的投影/店面;SLA新鮮+「as_of」。
操作:不變量度量,DLQ/redrive花花公子,重生店面。
18)售前支票清單
- 已定義了邊界內容及其合同(命令/事件)。
- 聚合物具有明顯的不變性,反轉和冪等指令。
- 現金交易-通過TSS/嚴格的交易;已啟用審計。
- 整合-通過ACL進行電路轉換和進化測試。
- 引入outbox/inbox、DLQ和安全重做。
- 投影實現了SLA的新鮮度,並具有拉格/穩定度量。
- 符合多重限額/限額和數據駐留。
- 可觀察性:通過不變量對「komanda→sobytiye→proyektsiya」進行跟蹤。
- 文檔:域語言,上下文圖,事件花花公子。
結論
iGaming內核中的DDD是難度分離的學科:上下文的清晰邊界,具有不變量的聚合,作為真理來源的事件,用於外部集成的ACL以及有意識的一致性選擇。這種方法使該平臺具有可擴展性,可靠性和適當的法規,加快了幻燈片的開發並降低了運營風險-即使流量、地理和產品線的快速增長也是如此。