Overview / Ledger / money
ADR 0030
Money is stored as a plain numeric amount plus a currency code
AcceptedLedger / money
Context
Every monetary value needs an amount and a currency. Two broad shapes were available: a Money value object that binds amount+currency into one type enforced everywhere, or a plain numeric column with a sibling currency code. There is also the perennial amount-precision choice: integer minor units (cents) vs a decimal/float in major units.
Decision
Store money at rest as a plain database numeric amount alongside a currency_code string, in major units, with no Money type in the schema — though the fee/currency calculators do use the ex_money Money value object during computation.
- Amount + currency columns. An amount is an ordinary numeric column; the currency is a sibling
currency_code(ISO code) on the same row. Pairing is by convention and by the currency-partitioned account model (ADR 0032), not by a wrapping type. - Major units, not minor-unit integers. Amounts are stored in major units (e.g. dollars), not integer cents.
- Precision: float on the legacy path, decimal on the new ledger. The capture-path ledger historically uses
:float; the double-entry projection (ADR 0029) uses:decimalto remove float rounding error going forward.
Consequences
- Positive: amounts are simple, directly queryable/aggregatable columns; the schema needs no Money-type serialization (the calculators convert to
ex_moneyMoneywhere currency-safe arithmetic helps, then back to a decimal for storage). - Neutral / to revisit: without a Money type, "an amount always travels with its currency" is a discipline, not a compiler guarantee, and nothing structurally prevents mixing currencies in a careless calculation (the currency-partitioned accounts of ADR 0032 are the real guard).
- Neutral / to revisit: major-units
:floaton the legacy path is prone to rounding; the new ledger's:decimalfixes this, but a float→decimal boundary exists while both ledgers run (ADR 0029).
Alternatives considered
- A Money value object as the storage representation: not adopted at rest — columns stay plain numeric +
currency_code;ex_money'sMoneyis used inside the calculators, not in the schema, so the pairing is enforced there and by per-currency accounts (ADR 0032) rather than by the stored type. - Integer minor units (cents): not adopted for storage — amounts are kept in major units; precision is instead addressed by moving the authoritative ledger to
:decimal. (Minor-unit integers remain the textbook alternative if the float/decimal split ever proves troublesome.)