ledger

module
v0.9.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 13, 2026 License: Apache-2.0

README


directory: ledger purpose: The ledger bounded context — facts, streams, claims, resolution, and the position projection derived from them. owner: "@FabioCaffarello" allowed:

  • domain/ — pure rules over kernel types
  • app/ — use cases and the ports they depend on
  • adapters/ — pure adapters only, such as in-memory stores and injected clocks
  • Imports of libs/kernel forbidden:
  • Importing another bounded context's domain or app (ADR-0013)
  • Any dependency needing a driver, broker, browser or SDK — those live in libs/ledger-
  • Storing a position, balance or any derived state; they are projections (ADR-0007)
  • Clocks, I/O, concurrency or binary floating point in domain/
  • Mutating or deleting an appended fact — corrections are new facts

libs/ledger

The first bounded context, and the vertical slice that validated everything above it. Facts are appended and never amended; every figure a caller receives is derived from them, with the trace that produced it.

Layer Holds May import
domain/ Facts, streams, claims, resolution, the position projection kernel
app/ Use cases and the ports they depend on kernel, own domain
adapters/ In-memory store, injected clock kernel, own domain, own app
storetest/ The conformance suite every app.Store must pass kernel, own domain, own app

storetest/ is exported rather than internal because app.Store is: an out-of-tree adapter that cannot run the suite is one nobody can hold to the contract. It lives here rather than in an adapter so that a second engine does not have to depend on the first merely to be tested (ADR-0034).

The dependency rule is ADR-0013's and is enforced by the layering analyser, not by review.

Infrastructure lives in another module

adapters/ here holds pure adapters only. Anything needing a driver, broker or SDK belongs in libs/ledger-<tech>, and the reason is dependency resolution rather than taste.

Go resolves dependencies per module, not per package. An adapters/postgres inside this module would put the Postgres driver into the module graph and go.sum of every consumer importing libs/ledger/domain — including consumers that import none of it. That would make Constitution §10 true at the package level and false at the level that decides what a consumer is actually coupled to.

State is never stored

A position is a projection over a fact stream at an as-of coordinate, computed on demand. There is no table of positions to fall out of step with the facts, because a stored balance is a second source of truth that will disagree with the first and give no indication which is wrong.

Explained[Position] or nothing at all: a projection that cannot explain itself does not compile (ADR-0012).

Claims, mints and resolution

A connector cannot know an EntityId and must not mint one — deriving identity from a ticker makes the ticker the primary key, and a reused ticker then merges two instruments silently inside an append-only ledger (ADR-0007).

So a connector emits a claim; minting an identity is itself a fact; and resolution is a derivation recorded in the ledger rather than a precondition of appending (ADR-0022).

The path, in the order it runs:

Step Use case What it may not do
Admit AcceptHoldingClaim resolve, or mint
Look UnresolvedClaims resolve, or mint — looking must not be what stops a claim waiting
Mint MintIdentity mint twice; a claim that already resolves is refused
Derive ObserveClaimedHolding mint, or derive from a claim that does not resolve

MintIdentity is the only thing here that appends an EntityMinted fact, and that is the decision rather than an implementation detail (ADR-0033). Admission cannot mint because an identity that came into existence because a stranger submitted a claim is an identity nobody chose; inspection cannot mint because the act of looking must not change the ledger.

Resolution decides sameness by a versioned per-scheme ruleset, applied before identity.Derive and never inside it. A rule exists only for a scheme whose issuing standard defines a canonical form, so isin has one and ticker cannot — deciding that PETR4 and PETR4.SA are one instrument is a merge, and merges are recorded as EntitiesIdentified, never performed (ADR-0007, ADR-0033).

Who is entitled to mint is not answered. A mint records a Source and an Interpreter and nothing verifies either, so the authority boundary today is the process boundary: whoever can call MintIdentity can mint. ADR-0033 records that at rung 6 rather than dressing it as something stronger.

Nobody is told about an unresolved claim. UnresolvedClaims makes it askable and nothing asks — a connector can still publish faithfully into silence if no operator looks. Who is told, and how, is operational and undecided; it is tracked on issue #57, which carries what remains of B-007 (ADR-0032).

Adding a payload

A new fact payload needs the ADR that sequences it, the published message in libs/contracts, and a codec with round-trip conformance in libs/ledger-wire — in both directions. A payload added without the second direction passes forever while silently dropping whatever it never learned to read.

Directories

Path Synopsis
adapters
clock
Package clock supplies knowledge time to the application layer.
Package clock supplies knowledge time to the application layer.
memory
Package memory is an in-memory Store, for tests and for the M6 vertical slice.
Package memory is an in-memory Store, for tests and for the M6 vertical slice.
Package app orchestrates the ledger domain.
Package app orchestrates the ledger domain.
Package domain is the pure core of the FDOS ledger.
Package domain is the pure core of the FDOS ledger.
Package storetest is the conformance suite every app.Store must pass.
Package storetest is the conformance suite every app.Store must pass.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL