Skip to content

🧠 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.

  • TryHold debits now
  • Commit finalises
  • Release refunds

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