Currency System — Mental Model¶
This page explains how to think about Currency in RevFramework.
It is not an API reference.
It is a conceptual map that helps you decide:
- Where does this behaviour belong?
- Which extension point should I use?
- What should I never touch?
If you understand this page, the Currency API will feel obvious.
The Core Idea¶
The core idea
Currency is a deterministic ledger with guarded mutation, not a UI counter.
A currency value is not “just a number you change”.
It is a ledger-backed state that must:
- validate intent
- enforce policy
- respect authority
- emit truthful events
- support rollback and replay
Everything in the system exists to protect that ledger.
The Single Source of Truth¶
ICurrencyService¶
The currency ledger is owned and mutated through ICurrencyService
ICurrencyService
It owns:
- balances
- mutation rules
- event emission
- orchestration guarantees (rollback, batching when supported)
Nothing else is allowed to:
- mutate balances
- fake deltas
- bypass policy or authority
- emit currency events
If something changes currency without going through the service, it’s wrong.
The Currency Mutation Model¶
Every currency change conceptually flows through the same logical stages:
Intent
↓
Validation
↓
Policy
↓
Authority
↓
Mutation
↓
Events / Audit
However:
These stages are conceptual, not a fixed runtime pipeline.
In practice the order is defined by decorator composition (via CurrencyFactories).
Example stack:
Caps → Audit → Authority → Idempotency → BatchEvents
Each decorator guards part of the mutation process.
Stage 1: Intent (The Request)¶
Question: “What is being requested?”
At this stage:
- inputs are validated
- amounts must be valid
- currency IDs must be valid
This is where programmer errors are rejected.
What belongs here¶
- argument validation
- request metadata (
reason,sourceId)
What does NOT belong here¶
- balance checks
- policy enforcement
- authority decisions
Intent is about shape, not permission.
Stage 2: Policy (Rules & Caps)¶
Question: “Is this value allowed?”
Policy is defined by CurrencyPolicy and evaluated via CurrencyCapRule.
Policy can:
- clamp values
- reject mutations
- require escrow globally
Design rule¶
Policy decides legality — it does not perform mutations.
Stage 3: Authority (Who May Act)¶
Question: “Is this caller allowed to do this?”
Authority is enforced via ICurrencyAuthority.
Authority:
- may block operations
- never changes amounts
- never emits events
This design keeps Currency multiplayer-ready.
Stage 4: Mutation (The Ledger Write)¶
Question: “Write the new balance.”
This is the only place where balances actually change.
Rules:
- mutations are deterministic within a given composed service stack
- overflows are detected
- insufficient funds fail cleanly
For a single operation:
If mutation fails, the ledger remains unchanged.
For orchestrations:
- earlier successful steps may require rollback
- rollback is best-effort unless escrow is used
Stage 5: Events & Audit (Observation)¶
After a successful mutation:
CurrencyDeltais emitted- optional audit entries are recorded
- batch events may be emitted
Design rule¶
Events reflect what actually happened — never intent.
Audit (Optional, Observational)¶
Audit:
- records successful mutations
- stores before/after/delta
- is read-only to consumers
Audit is a ledger of history, not a source of truth.
Transactions¶
CurrencyTxn¶
- ordered execution
- best-effort rollback on failure
- optional batch emission
Rollback may be blocked by policy, authority, escrow, or idempotency depending on stack composition.
CurrencyHoldTxn¶
Uses escrow holds for stronger guarantees.
Process:
Acquire holds → Preflight → Apply credits → Commit tokens
Exchange¶
Exchange performs:
Debit source → Credit destination
Quoting is non-mutating and rule-based.
Execution uses best-effort rollback on credit failure.
Escrow¶
Escrow supports reservations and delayed confirmation.
TryHold → debit immediately
Commit → finalize (logical no-op; funds already debited at hold time) Release → refund
Expired holds are processed via ExpireStale.
Batching¶
Batching affects event emission only, not mutation behavior.
Batch events require the service stack to include:
CurrencyFactories.WithBatchEvents()
Adapters & External Systems¶
Currency does not directly depend on:
- UI
- Inventory
- Save systems
- Networking
External systems adapt to Currency, not the other way around.
What Never Belongs Where¶
What never belongs where
- UI must not mutate currency directly
- Events must not imply success before mutation
- Policy must not write balances
- Authority must not change amounts
- Audit must not become a dependency
- External code must not emit currency deltas
The One‑Sentence Model¶
The one-sentence model
Currency is a guarded ledger where intent, permission, legality, execution, and observation are strictly separated.
TL;DR¶
TL;DR
- One service owns truth
- Policy decides legality
- Authority decides permission
- Ledger writes are deterministic
- Events reflect reality
- Everything else adapts around the service