ADR-005: Wallet Topology Bucket Ledger Model
Status
Accepted
Date
2026-04-23
Owners
- Platform Backend
- Wallet Domain
Affected Services
wallet_servicerolling_servicepromotion_servicegame_servicegatewayadmin_service
Related Docs
docs/specs/wallet/2026-04-23-ruby-wallet-split-structure.mddocs/plans/wallet/2026-04-23-ruby-wallet-split-implementation.mddocs/runbooks/wallet/ruby-wallet-topology-rollout.mddocs/services/wallet-service.mddocs/architecture/data-ownership.md
Context
The wallet domain must support Ruby Wallet split rules before launch:
- configurable wallet structure
- configurable sports, live, and slots betting policies
- separate normal and bonus buckets by wallet group
- shared withdrawable and points buckets
- scoped coupon grants
- deterministic settlement, rollback, transfer, and rolling attribution
The current flat wallet-column model is too rigid for this. Adding more player columns for every new wallet shape would make future wallet policy changes expensive and error-prone, and it would keep money state coupled to player profile storage.
The system needs a durable model where wallet shape is configurable data, wallet balances are wallet-owned, and every money movement is auditable.
Decision
wallet_service will own a topology-driven wallet model:
wallet_topologydefines versioned wallet shape.wallet_bucket_typedefines bucket types for a topology.wallet_bucketstores current player balances by bucket type.wallet_coupon_grantstores coupon money and coupon-specific constraints.wallet_policystores versioned declarative policy documents.wallet_bet_authorizationstores the immutable funding decision for each accepted bet.wallet_ledgerstores append-only money movement records.
Wallet bucket type codes are topology data, not hard-coded money-column names. Contracts may contain stable role and provider enums, but the active wallet shape must come from topology and policy documents.
Every wallet command must resolve authoritative topology and policy server-side. Callers may pass facts such as player, bet, amount, provider type, provider ID, and selected wallet source when required, but callers must not decide funding mode, deduction order, settlement destination, or rolling attribution.
Every accepted money movement must store:
- topology code and version
- policy version
- policy snapshot where deterministic replay is required
- ledger rows for the actual balance movement
Consequences
Positive:
- wallet shape can evolve without adding player balance columns
- sports/casino split can launch as
RUBY_SPLIT_V1 - future live/slots/provider-specific wallet splits are possible through topology and policy changes
- settlement and rollback are deterministic from stored authorization data
- auditability improves through append-only ledger records
wallet_serviceremains the only money writer
Negative:
- implementation is larger than extending the current flat fields
- policy validation becomes a required production capability
- topology activation requires strict safety checks
- tests must cover topology, policy, and ledger behavior before rollout
Constraints
- No new canonical wallet implementation may write flat player wallet columns.
- Ledger rows are immutable after commit.
- Policy documents must be declarative data and must pass versioned schema validation before activation.
- Topology activation must fail when unresolved balances, coupon grants, rollings, unsettled bets, or transfers would become unreachable.
- Settlement and rollback must use stored authorization breakdowns, not current balances or current policy.
- Coupon money must remain grant-scoped, not collapsed into one generic coupon bucket.
- Points cannot be bet or withdrawn directly unless a future accepted spec explicitly changes that rule.
Follow-Up
- Implement the wallet split structure according to the related spec and plan.
- Add topology validation tests before money command implementation.
- Update stable service and architecture docs after the implementation is merged.
- Promote the wallet rollout runbook to
Readybefore enabling the new wallet model in any shared environment.