Appendix: Implementation notes

This appendix collects “how the sausage is made” notes for LedgerLoom:

design decisions, determinism, and a quick tour of the engine.

LedgerLoom Engine v0.1

As of v0.1.4, LedgerLoom includes a small reusable core under:

src/ledgerloom/engine/

The goal is to keep the chapters readable while extracting the reusable mechanics:

  • Chart of Accounts (COA) schema + validation

  • Turning journal entries into a postings fact table

  • Derived views (balances by account / period / segment)

  • Invariant checks (the double-entry constraint, etc.)

The engine is intentionally not a database. Instead, it produces “database-shaped” tables (CSV/JSON artifacts) that are easy to inspect, diff, and load into real systems. The engine itself does not perform file I/O; chapter runners handle writing artifacts.

Public surface area

These are the main objects intended to stay stable as the book grows:

  • Money (engine/money.py): currency + integer cents (safe arithmetic)

  • ChartOfAccounts (engine/coa.py): accounts + rollups + segment dimensions

  • Posting and JournalEntry (engine/ledger.py): canonical “ledger events”

  • LedgerEngine (engine/ledger.py): compiles + materializes common views

At a high level, Chapter runners do:

  1. Build (or load) a COA

  2. Generate a small set of JournalEntry objects

  3. Compile them into a postings fact table

  4. Materialize one or more views and write artifacts

Determinism

LedgerLoom treats each chapter as a deterministic pipeline:

  • A --seed flag controls any synthetic data generation

  • Postings are sorted stably by (date, entry_id, line_no)

  • Views are sorted by their grouping keys

  • manifest.json records sha256 for each artifact (reproducibility)

The tests prefer “shape and invariants” checks where possible, and golden files only when the output is meant to be a fixed reference.

Chapter boundaries

Rule of thumb for where code should live:

  • If it’s accounting mechanics you’ll reuse across chapters (posting compilation, rollups, balance aggregation, invariants), it belongs in ledgerloom.engine.

  • If it’s narrative (data generation, exposition, and chapter-specific outputs), keep it in the chapter runner.

This keeps the engine small and teachable, while allowing the “book” to remain clear and focused.