DDD im iGaming-Kern
Die iGaming-Plattform ist ein komplexes Domain-System an der Schnittstelle von Finanzen, Unterhaltung und Compliance. DDD hilft, Komplexität zu halten: Es hebt bounded contexts hervor, erfasst ubiquitous language, schützt Invarianten mit Aggregaten, vereinfacht Integrationen durch Anti-Korruptions-Schichten und macht das Systemverhalten durch Domain-Events transparent.
1) Domainkarte und bounded contexts (strategisches Design)
Empfohlene Zerlegung:- Player/KYC Context - Registrierung, Verifizierung, Grenzen für verantwortungsvolles Spielen, KYC/AML-Status.
- Wallet/Ledger Context - Bilanzen, Reservierungen, Buchungen, Multi-Currency, Kurse.
- Betting Context - Gebote/Tickets, Paar/Ergebnisse, Angebote, Abrechnung (settlement), Stornierung.
- Casino/Game Round Context - Sessions, Runden, Spins, RTP-Kontrolle, Einsatzlimits.
- Bonus/Promo-Kontext - Bonusregeln, Vager, Erwerb von Bonusgeldern, Anti-Missbrauch.
- Risiko/Betrug Kontext - Scoring, Verhaltenssignale, Auslöser von Sperren/Timeouts.
- Zahlungskontext - Ein-/Auszahlungen, Status von Zahlungsgateways, Chargeback-Ereignisse.
- Compliance/Berichtskontext - Berichte an Aufsichtsbehörden, Sanktionslisten, Audit.
- Content/Provider Integration Context - Integration mit Spieleanbietern, Katalogen, Technik. Status.
- Analytics/Read Models - Projektionen und Vitrinen für Produktlesungen.
2) Ubiquitous Sprache: der Kern der Begriffe
Spieler (Spieler), Session (Session), GameRound (Runde), Wette/Ticket (Wette),
Ledger Entry, Hold/Reserve, Settlement,
Bonus Credit / Bonus Balance, Wagering Requirement (Вейджер),
KYC Tier, Limit (Einzahlung/Sitzung/Verlust), Selbstausschluss,
Provider Game, RTP Window, Risk Flag, Compliance Case.
Diese Namen werden gleichermaßen in Code, DB, Dokumentation, Tests und Schnittstellen verwendet.
3) Aggregate und Invarianten (taktisches Design)
3. 1 Wallet (Aggregate: `Wallet`)
Invarianten:- Die Bilanz geht nicht ins Minus.
- Reserve + verfügbar ≤ Gesamtbilanz.
- Die Verdrahtung ist atomar und idempotent (durch 'operation _ id').
- `Wallet. Reserve(amount, reason, op_id)` → `WalletReserved`
- `Wallet. Commit(op_id)` → `WalletCommitted`
- `Wallet. Rollback(op_id)` → `WalletRolledBack`
Grenze: Wallet weiß nichts über Bet/Bonus; es dient Transaktionen und Reserven.
3. 2 Bet/Ticket (Aggregate: `Bet`)
Invarianten:- Das Gebot kann nur im aktiven Angebotsfenster angenommen werden; Betrag des ≤ Limits des Spielers/der Sitzung.
- Nach 'Settled' ist der Status' finalisiert'; Eine Neuberechnung ist nur durch Ausgleichsvorgänge (void/recalc) mit eindeutiger Prüfung zulässig.
- `Bet. Place(player_id, amount, price, op_id)` → `BetPlaced` (требует Wallet. Reserve)
- `Bet. Settle (outcome, payout) '→' BetSettled'(erfordert Wallet. Commit/Release)
- `Bet. Void(reason)` → `BetVoided`
Grenze: Bet „klettert“ nicht in Wallet - wird über Domain-Befehle/Orchestrierung angesprochen.
3. 3 GameRound (Aggregate: `Round`)
Invarianten:- Jeder Spin/jede Runde hat eine eindeutige' round _ id 'und einen zugehörigen Einsatz-/Gewinnbetrag.
- Das RTP-Fenster überschreitet nicht die vorgegebenen Schwellenwerte (auf Anbieterebene + lokale Regeln).
- `Round. Started`, `Round. Staked`, `Round. Resulted`, `Round. Closed`.
3. 4 Bonus (Aggregate: `BonusGrant`)
Invarianten:- Vager reduziert sich erst ab dem gültigen Umsatz, Bonusabbuchungen gehen nicht in die Belastung.
- Es ist nicht möglich, den Bonus und das Echtgeld gleichzeitig nach einer anderen Prioritätsregel abzuschreiben.
- `BonusGranted`, `BonusWagered`, `BonusExpired`, `BonusConverted`.
4) Orchestrationen, Sagas und Konsistenz
Synchron (CP): Wettannahme und Mittelreserve - ein einziger Weg: 'Bet. Place` → `Wallet. Reserve'(über Domain-Team/Orchestrator mit Deadline).
Asynchron (EC): Wettberechnung, Bonusberechnung, Analytik - durch Events + Outbox.
TCC-Variante: 'TryReserve' (hold), 'Confirm' (commit), 'Cancel' (rollback) für monetäre Effekte.
Idempotenz: Alle Teams tragen 'operation _ id', die Consumer 'inbox'.
5) Anti-Korruptions-Schichten (ACL) und Integrationen
Provider ACL: Übertragung der Provider-Events' SpinResult', 'BonusWin' in die interne' Runde. Resulted`, `BonusWagered`. Diagramme und Versionen sind innerhalb der ACL.
PSP ACL: Normalisierung des Zahlungsstatus, Idempotenz durch „psp _ tx _ id“, Übersetzung in „LedgerEntry“.
Compliance ACL: Integration mit Sanktionslisten/PER - im externen Kontext; innerhalb der Domain gelangen nur normalisierte' ScreeningUpdated'.
Regel: Externe Wörterbücher/Formate „sickern“ nicht in den Kernel.
6) Projektionen und Lesemodelle
Spielerprofil Lesemodell: KYC-Status, Limits, aktive Boni, frische Transaktionen.
Balances Read Model: Schnelle Lesungen für UI/Marketing; Quelle ist 'Wallet' des Ereignisses.
Bet History Read Model: Paginierung nach Datum/Spiel; Quelle ist „BetPlaced/Settled“.
Compliance Reports: materialisierte Ansichten nach Tenant/Region.
Alle Projektionen sind idempotente upsert's mit Versionierung und 'as _ of/freshness'.
7) Multi-Tenant und Multi-Region
Alle Schlüsseleinheiten tragen 'tenant _ id' und (falls erforderlich) 'region'.
Datengrenzen: Spieler, Brieftasche, Wetten - „Home“ -Region; regionalübergreifend nur Aggregate/Berichte.
Fairness/Quoten: Limits pro Team/sec und Redraves pro Tenant.
Residency/Compliance: Personenbezogene Daten und Buchungen verlassen die Region nicht.
8) Konsistenzauswahl (PACELC) nach Kontext
Wallet/Ledger - Strong/CP: Linearisierte Buchungen, Quorum der Einträge.
Bet acceptance - Synchrone Bestätigung (CP) + schnelle Lesemodelle für die Benutzeroberfläche.
Siedlung/Bonus/Analytics - EC mit deterministischer Merge/Kompensation.
KYC/Compliance - kann ein EC für Status sein, aber „blockierende“ Regeln werden synchron angewendet.
9) Domain-Events: Verträge und Version
Minimaler Feldsatz: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"
}
Regeln:
- Back/forward-compat Schemata; Evolution durch „schema _ version“.
- 'outbox' in einer Transaktion mit Domänenänderungen; Veröffentlichung von Battles mit Backoff.
10) Beispiel-Stream „Wetten mit Bonus“ (verbale Sequenz)
1. `Bet. Place'(Team) → Überprüfung der Spielerlimits und der → 'Wallet Bonusregeln. Reserve(real+bonus_equiv, op_id)`
2. 'BetPlaced' (Ereignis) → Read Model aktualisiert 'Offene Wetten'
3. Der Anbieter veröffentlicht das Ergebnis → ACL → 'Round. Resulted`
4. Der Orchestrator rechnet: 'Bet. Settle(outcome,payout)` → `Wallet. Commit (op_id) 'und, wenn Sie gewinnen,' BonusWagered '→ eine mögliche Umwandlung des Bonus in real.
5. 'BetSettled' → Projektionen der Geschichte und Bilanzen, Berichterstattung.
11) Invarianten und Testrichtlinien
Schlüsselinvarianten:- Die Summe aller 'LedgerEntry' auf der Brieftasche ist gleich dem Saldo; keine negativen Rückstände.
- Sie können bei aktivem Selbstausschluss/eingefrorenem KYC-Status keine Wette annehmen.
- Vager kann nur abnehmen und nicht „im Minus“ schwingen.
- Settlement ändert nichts am Status der bereits finalisierten Wette - nur durch 'Void/Recalc' + Kompensationsbuchung.
- Eigenschaftsbasierte Tests von Wallet-Invarianten und Wetten.
- Die Konturen des Chaos: Provider-Verzögerungen, PSP-Ausfälle, Outbox/DLQ-Redrives.
- Schemasteuerung: Ereignismigrationen, Backfill-Projektionen.
12) Telemetrie und Audit
Metriken: p95/p99 auf PlaceBet/Reserve/Commit, Ausfallanteil nach Limits/CUS, DLQ-Rate, Redrive-Erfolg, Lag-Projektionen.
Tracing: Spans von „komanda→agregat→outbox→konsyumer→proyektsiya“, Tags von „tenant _ id“, „operation _ id“, „saga _ id“.
Audit: Unveränderliches Protokoll von Domänenaktivitäten, vergleichbar mit regulatorischen Anforderungen.
13) Speicherschema (vereinfacht)
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)
Die Versionierung auf Aggregaten ('version') schützt vor verlorener Aktualisierung bei kompetitiver Aufzeichnung.
14) Beispielbefehle API (Pseudo)
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":"..." }
Alle Befehle sind mit 'operation _ id' für idempotence, die Antworten sind mit 'as _ of '/' version'.
15) Sicherheit und Compliance
RLS/ACL: alle Anfragen - im Kontext 'tenant _ id', Zugriff nach Rolle.
PII-Minimierung: Trennung von Domain-Events von personenbezogenen Daten; Maskierung in DLQ/Logs.
Regulatorische Berichte: Projektionen mit unveränderlichen Hash-Signaturen durch Zeitfenster.
16) Typische Fehler
Starke Konnektivität zwischen Kontexten (Wallet kennt Bet/Bonus direkt).
Dual-Write in verschiedene Kontexte ohne Sagen/Outbox → Fehlausrichtung von Salden und Status.
Keine Idempotenz von Teams und Konsumenten → Doppelbuchungen/Abrechnungen.
Der Fluss von Anbieterverträgen in das Domänenmodell (schwieriger zu migrieren).
Ein „gigantisches“ Gerät (der Player enthält alles) → Sperren, niedrige Durchgänge.
Es gibt keine offensichtlichen Invarianten - sie können nicht überprüft und überwacht werden.
17) Schnelle Rezepte
Start: Fixieren Sie Ubiquitous Language und Kontextgrenzen; Invarianten dokumentieren.
Geld: Wallet/Ledger - CP, Quorum Records, TCC für externe Effekte.
Preise: synchroner Empfang + asynchrone Berechnung, alles durch Ereignisse und Outbox; Idempotenz ist überall.
Boni: separate Einheit mit klarer Priorität für Abschreibungen und Vager.
Integrationen: immer über ACL + Schemen/Versionen; keine „Rohstoff“ payload's im Kern.
Lesungen: Projektionen/Schaufenster auf die Bedürfnisse des Produkts; SLA Frische + „as _ of“.
Betrieb: Invarianten Metriken, DLQ/redrive Playbooks, rebuild Vitrinen.
18) Checkliste vor dem Verkauf
- Bounded Contexts und ihre Verträge (Teams/Events) sind definiert.
- Aggregate haben explizite Invarianten, Versionierung und idempotente Befehle.
- Geldtransaktionen - durch TCC/strikte Transaktionalität; Audit ist aktiviert.
- Integrationen - über ACL mit Schaltungsversionierung und Evolutionstests.
- Outbox/Inbox, DLQ und Secure Redrive implementiert.
- Projektionen implementieren SLA Frische, es gibt lag/staleness Metriken.
- Multi-Tenant-Quoten/-Limits und Datenresidenz werden eingehalten.
- Beobachtbarkeit: Tracing „komanda→sobytiye→proyektsiya“, Alerts durch Invarianten.
- Dokumentation: Domänensprache, Kontextdiagramme, Incident Playbooks.
Schlussfolgerung
DDD im iGaming-Kern ist eine Disziplin der Komplexitätsteilung: klare Grenzen von Kontexten, Aggregate mit Invarianten, Ereignisse als Quelle der Wahrheit, ACLs für externe Integrationen und bewusste Konsistenzentscheidungen. Dieser Ansatz macht die Plattform skalierbar, zuverlässig und Compliance-konform, beschleunigt die Entwicklung und reduziert die operativen Risiken - selbst bei einem schnellen Wachstum von Traffic, Geografien und Produktlinien.