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以及有意识的一致性选择。这种方法使该平台具有可扩展性,可靠性和适当的法规,加快了幻灯片的开发并降低了运营风险-即使流量、地理和产品线的快速增长也是如此。