🧠 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¶
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¶
There is exactly one authority on currency truth:
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 Pipeline¶
Every currency change flows through the same conceptual stages:
Intent
↓
Validation
↓
Policy
↓
Authority
↓
Mutation
↓
Events / Audit
This pipeline applies to:
- Credit
- Debit
- SetBalance
- Transfer
- Exchanges
- Transactions
Decorators exist to guard specific stages of this flow.
🔍 Stage 1: Intent (The Request)¶
Question: “What is being requested?”
At this stage: - inputs are checked for basic validity - amounts must be non-negative - 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 never mutates directly — it decides what is allowed.
What belongs here:¶
- min/max caps
- clamp vs fail behaviour
- escrow requirements
What does NOT belong here:¶
- refunds
- retries
- UI logic
Policy answers legality, not execution.
🔐 Stage 3: Authority (Who May Act)¶
Question: “Is this caller allowed to do this?”
Authority is enforced via ICurrencyAuthority.
This stage answers: - can this owner be mutated? - can this transfer occur?
Design rule:¶
Authority gates permission, not arithmetic.
Authority: - may block operations - never changes amounts - never emits events
This is what makes 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 - overflows are detected - insufficient funds fail cleanly
If this stage fails, that individual mutation does not change state. Higher-level orchestrations (e.g., transactions) may perform rollback of prior steps.
If it fails, nothing changes.
📊 Stage 5: Events & Audit (Observation)¶
Question: “What should observers be told?”
After a successful mutation:
- CurrencyDelta is emitted
- optional audit entries are recorded
- batch events may be emitted
Design rule:¶
Events reflect what actually happened — never intent.
This guarantees: - UI never lies - analytics stay correct - replays are deterministic
🧾 Audit (Optional, Observational)¶
Audit is not validation.
Audit: - records successful mutations - stores before/after/delta - is read-only to consumers
Mental model:¶
Audit is a ledger of history, not a source of truth.
If audit is disabled, the system still behaves identically.
📦 Transactions (Multiple Mutations)¶
CurrencyTxn¶
A transaction is a controlled orchestration of mutations.
It guarantees: - Orchestrated multi-step application - Best-effort rollback on first failure - Single batch event when supported
Note: Rollback may be blocked by policy, authority, escrow requirements, or idempotency depending on the composed stack.
Design rule:¶
Transactions coordinate multiple calls — they do not bypass rules.
Each step still flows through: - policy - authority - mutation
Transactions manage ordering and rollback, not legality.
🔁 Exchange (Two Mutations, One Intent)¶
Currency exchange is: - a debit - followed by a credit - wrapped in a single intent
Quoting is pure and non-mutating.
Execution is orchestrated: - debit first - credit second - best-effort rollback on credit failure
Mental model:¶
Exchange is not magic — it’s a composed transaction.
🔐 Escrow (Deferred Finality)¶
Escrow exists to support: - reservations - delayed confirmation - rollback-safe holds
Design rule:¶
Escrow debits immediately but defers finality.
TryHolddebits nowCommitfinalisesReleaserefunds
Escrow never changes policy or authority — it changes timing.
🧩 Batching (Observation Control)¶
Batching exists only to control event emission.
It does NOT: - change mutation order - change policy behaviour - change authority checks
Mental model:¶
Batching affects observers, not state.
🧰 Adapters & External Systems¶
Currency never talks directly to: - UI - Inventory internals - Save systems - Networking code
It exposes: - a stable service interface - explicit capability seams
Everything else adapts to Currency — not the other way around.
🚫 What Never Belongs Where¶
- UI should never mutate currency directly
- Events should never imply success before mutation
- Policy should never write balances
- Authority should never change amounts
- Audit should never be a dependency
- Anything outside the service should never emit deltas
If a component crosses these lines, it’s wrong.
🧠 The One-Sentence Model¶
Currency is a guarded ledger where intent, permission, legality, execution, and observation are strictly separated.
If you keep that sentence in your head, you’ll extend the system correctly.
TL;DR¶
- One service owns truth
- Mutations flow through a strict pipeline
- Policy decides legality
- Authority decides permission
- Ledger writes are deterministic
- Events reflect reality
- Everything else adapts around the service