DDD en el núcleo iGaming
La plataforma iGaming es un complejo sistema de dominio en la unión de finanzas, entretenimiento y cumplimiento. DDD ayuda a mantener la complejidad: resalta los contextos bounded, fija el lenguaje ubiquitous, protege los invariantes con agregados, simplifica la integración a través de capas anticorrupción y hace transparente el comportamiento del sistema gracias a los eventos de dominio.
1) Mapa de dominios y contextos bounded (diseño estratégico)
Descomposición recomendada:- Player/KYC Context - registro, verificación, límites de juego responsable, estados KYC/AML.
- Wallet/Ledger Context - balances, reservas, cableado, multidivisa, cursos.
- Betting Context - apuestas/tickets, par/resultados, cotizaciones, cálculo (settlement), cancelación.
- Casino/Game Round Context - sesiones, rondas, giros, control de RTP, límites de apuestas.
- Bonus/Promo Context - reglas de bonificación, wager, fondos de bonificación de equipaje, anti-Abuse.
- Risk/Fraud Context - puntuación, señales de comportamiento, disparadores de bloqueo/tiempo de espera.
- Payments Context - depósitos/retiros, estados de pasarelas de pago, eventos de chargeback.
- Compliance/Reporting Context - Informes a reguladores, listas de sanciones, auditorías.
- Content/Provider Integration Context - Integración con proveedores de juegos, directorios, tecnología. estados.
- Analytics/Read Models - proyecciones y escaparates bajo lecturas de productos.
2) Lenguaje Ubiquitous: el núcleo de los términos
Player (Jugador), Sesión (Sesión), GameRound (Ronda), Apuesta/Ticket (Apuesta),
Ledger Entry (Cableado), Hold/Reserve (Reserva), Settlement (Cálculo),
Bonus Credit / Bonus Balance, Wagering Requirement (Вейджер),
KYC Tier, Limit (depósito/sesión/pérdida), Self-Exclusion,
Provider Game, RTP Window, Risk Flag, Compliance Case.
Estos nombres se usan igualmente en código, DAB, documentación, pruebas e interfaces.
3) Agregados e invariantes (diseño táctico)
3. 1 Wallet (Aggregate: `Wallet`)
Invariantes:- El balance no va a menos.
- Reserva + Saldo total ≤ disponible.
- El cableado es atómico e idempotente (por 'operation _ id').
- `Wallet. Reserve(amount, reason, op_id)` → `WalletReserved`
- `Wallet. Commit(op_id)` → `WalletCommitted`
- `Wallet. Rollback(op_id)` → `WalletRolledBack`
Límite: Wallet no sabe acerca de Bet/Bonus; sirve operaciones de cableado y reservas.
3. 2 Bet/Ticket (Aggregate: `Bet`)
Invariantes:- La apuesta sólo se puede aceptar en la ventana de cotización activa; suma ≤ límite de jugador/sesión.
- Después de 'Settled', el estado es «finalizado»; sólo se permite el nuevo cálculo a través de operaciones compensatorias (void/recalc) con una auditoría clara.
- `Bet. Place(player_id, amount, price, op_id)` → `BetPlaced` (требует Wallet. Reserve)
- `Bet. Settle (outcome, payout) '→' BetSettled '(requiere Wallet. Commit/Release)
- `Bet. Void(reason)` → `BetVoided`
Border: Bet no «trepa» a Wallet - accede a través de comandos de dominio/orquestación.
3. 3 GameRound (Aggregate: `Round`)
Invariantes:- Cada giro/ronda tiene un 'round _ id' único y una cantidad de apuesta/ganancia asociada.
- La ventana RTP no supera los umbrales especificados (a nivel de proveedor + reglas locales).
- `Round. Started`, `Round. Staked`, `Round. Resulted`, `Round. Closed`.
3. 4 Bonus (Aggregate: `BonusGrant`)
Invariantes:- El Vager sólo se reduce a partir de la facturación válida, los cargos de bonificación no se adeudan.
- No es posible cancelar el bono y los fondos reales al mismo tiempo no por la regla de prioridad.
- `BonusGranted`, `BonusWagered`, `BonusExpired`, `BonusConverted`.
4) Orquestas, sagas y coherencia
Sincronizado (CP): aceptar la apuesta y la reserva de fondos es un único camino: 'Bet. Place` → `Wallet. Reserve '(a través de un comando de dominio/orquestador con un deadline).
Asíncrono (EC): cálculo de la apuesta, acumulación de bonificaciones, análisis - a través de eventos + outbox.
La opción TCC es: 'TryReserve' (hold), 'Conflm' (commit), 'Cancel' (rollback) para efectos monetarios.
Idempotencia: todos los comandos llevan 'operation _ id', los consumers son 'inbox'.
5) Capas anticorrupción (ACL) e integración
Provider ACL: transmisión de eventos providenciales de 'SpinNat', 'BonusWin' a la 'Round' interna. Resulted`, `BonusWagered`. Esquemas y versiones - dentro de la ACL.
ACL PSP: normalización de los estados de pago, idempotencia por 'psp _ tx _ id', transferencia a 'LedgerEntry'.
Compliance ACL: integración con las listas de sanciones/RR - en un contexto externo; sólo los 'ScreeningUpdated' normalizados entran dentro del dominio.
Regla: los diccionarios/formatos externos no se «filtran» dentro del núcleo.
6) Proyecciones y Modelos de Lectura
Player Profile Read Model: estados KYC, límites, bonos activos, transacciones nuevas.
Balances Read Model: lecturas rápidas para IU/marketing; la fuente es 'Wallet' del evento.
Bet History Read Model: paginación por fechas/juegos; la fuente es 'BetPlaced/Settled'.
Informes de Compliance: representaciones materializadas por tenante/región.
Todas las proyecciones son idempotentes upsert's con versionamiento y 'as _ of/freshness'.
7) Multi-tenant y multi-región
Todas las entidades clave llevan 'tenant _ id' y (si es necesario) 'region'.
Límites de datos: jugador, billetera, apuestas - región «hogar»; sólo agregados/informes transversales regionales.
Fairness/cupos: límites para comandos/segundos y redrastros por tenantes.
Residencia/cumplimiento: los datos personales y los cableados no salen de la región.
8) Selección de consistencia (PACELC) por contexto
Wallet/Ledger - Strong/CP: cableado linerizable, quórum de registros.
Bet acceptance - Confirmación sincrónica (CP) + Modelos de lectura rápida para IU.
Settlement/Bonus/Analytics - EC con merge/compensaciones deterministas.
KYC/Compliance - puede ser EC para los estados, pero las reglas de «bloqueo» se aplican sincrónicamente.
9) Eventos de dominio: contratos y versión
Conjunto mínimo de campos: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"
}
Reglas:
- Esquemas back/forward-compat; evolución a través de 'schema _ version'.
- 'outbox' en una transacción con cambios de dominio; publicación de batches con backoff.
10) Ejemplo de flujo «Apuesta con bonificación» (secuencia verbal)
1. `Bet. Place '(equipo) → comprobar los límites del jugador y las reglas del bono →' Wallet. Reserve(real+bonus_equiv, op_id)`
2. 'BetPlaced' (evento) → Read Model actualiza 'Apuestas abiertas'
3. El proveedor publica el resultado de la → ACL → 'Round. Resulted`
4. El orquestador cuenta: 'Bet. Settle(outcome,payout)` → `Wallet. Commit (op_id) 'y, al ganar,' BonusWagered '→ una posible conversión del bono en real.
5. 'BetSettled' → proyección de la historia y los balances, reporting.
11) Invariantes y política de pruebas
Invariantes clave:- La suma de todos los 'LedgerEntry' por billetera es igual al saldo; no hay residuos negativos.
- No se puede aceptar una apuesta cuando el estado KYC está activo por sí solo/congelado.
- El Vager sólo puede disminuir y no oscilar «en negativo».
- Settlement no cambia el estado de la apuesta ya finalizada - sólo a través de 'Void/Recalc' + cableado compensatorio.
- Pruebas property-based de invariantes de billetera y apuestas.
- Circuitos de caos: retrasos del proveedor, fallas de PSP, redrays de outbox/DLQ.
- Control de esquemas: migraciones de eventos, proyecciones de backfill.
12) Telemetría y auditoría
Métricas: p95/p99 en PlaceBet/Reserve/Commit, porcentaje de fallas por límites/CUS, tasa DLQ, éxito de redrive, proyección de lag.
Treking: durmiendo «komanda→agregat→outbox→konsyumer→proyektsiya», etiquetas 'tenant _ id', 'operation _ id', 'saga _ id'.
Auditoría: registro de acciones de dominio inmutable, comparable a los requisitos regulatorios.
13) Esquema de almacenamiento (simplificado)
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)
La versificación en agregados ('version') protegerá contra la actualización perdida cuando se realiza una grabación competitiva.
14) Ejemplo de API de comandos (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":"..." }
Todos los comandos son con 'operation _ id' para idempotencia, las respuestas son con 'as _ of '/' version'.
15) Seguridad y cumplimiento
RLS/ACL: todas las solicitudes - en el contexto de 'tenant _ id', acceso por roles.
PII-minimización: separación de los eventos de dominio de los datos personales; enmascaramiento en DLQ/logs.
Informes regulatorios: proyecciones con firmas hash inmutables en las ventanas de tiempo.
16) Errores típicos
Fuerte conectividad entre contextos (Wallet conoce directamente Bet/Bonus).
Dual-write a diferentes contextos sin sagas/outbox → desajustar balances y estados.
Falta de idempotencia de los equipos y consumidores → toma de cableado/cálculo.
Los contratos de proveedores pasan al modelo de dominio (es más difícil migrar).
Una unidad «gigante» (Player incluye todo) → bloqueo, bajo throughput.
No hay invariantes evidentes - no se pueden comprobar y monitorizar.
17) Recetas rápidas
Inicio: bloquee el lenguaje Ubiquitous y los límites contextuales; documentar invariantes.
Dinero: Wallet/Ledger - CP, registros de quórum, TCC para efectos externos.
Apuestas: recepción sincrónica + cálculo asíncrono, todo a través de eventos y outbox; la idempotencia está en todas partes.
Bonificaciones: una unidad separada con una prioridad clara de cargos y un vager.
Integraciones: siempre a través de ACL + esquemas/versiones; sin «materias primas» payload's en el núcleo.
Lecturas: proyecciones/vitrinas en las necesidades del producto; SLA de frescura + 'as _ of'.
Operación: métricas de invariantes, DLQ/redrive playbooks, escaparates rebuild.
18) Lista de verificación antes de la venta
- Se definen los contextos bounded y sus contratos (comandos/eventos).
- Los agregados tienen invariantes explícitos, versionados y comandos idempotentes.
- Transacciones monetarias - a través de CTS/transacciones estrictas; auditoría habilitada.
- Integraciones - a través de ACL con versionamiento de circuitos y pruebas de evolución.
- Implementado outbox/inbox, DLQ y redrive seguro.
- Las proyecciones implementan un SLA de frescura, hay métricas de lag/staleness.
- Se respetan las cuotas/límites multi-tenantes y la residencia de datos.
- Observabilidad: treysing «komanda→sobytiye→proyektsiya», alertas por invariantes.
- Documentación: lenguaje de dominio, diagramas de contexto, playbooks de incidentes.
la Conclusión
DDD en el núcleo iGaming es una disciplina de división de complejidad: límites claros de contextos, agregados con invariantes, eventos como fuente de verdad, ACL para integraciones externas y selección consciente de consistencia. Este enfoque hace que la plataforma sea escalable, confiable y compatible con las regulaciones, acelera el desarrollo de fich y reduce los riesgos operativos, incluso con el rápido crecimiento del tráfico, las geografías y la línea de productos.