Skip to content

ADR 0003: Reference catalogs are seeded into `settings` as typed JSON, not into new tables

  • Status: Accepted, 2026-08-28

Bookend ships four reference catalogs: the document taxonomy, the canonical field catalog, the reconciliation rule catalog and the per-document execution templates. All four are versioned reference data that ships with each release, that administrators tune from the Settings screens (rule thresholds, execution templates) and that must be recorded on every reconciliation run. The business schema is deliberately small, and derived or reference data is kept out of new business tables where it can be.

Each catalog is one row in settings with value_type = 'json':

Key Content Source file
documents.taxonomy document types with codes, display names, execution flag db/seeds/document-types.json
fields.catalog canonical field names, value kinds, typical source documents db/seeds/canonical-fields.json
rules.catalog rule IDs, kind, severity, fields, thresholds used, doc page db/seeds/rules.json
execution.templates per-document-type signature, initials, date and notary requirements db/seeds/execution-templates.json

db/seeds/generate-seeds.sh (and its PowerShell twin) renders the JSON into both dialects’ 9xx_seed_*.sql scripts so PostgreSQL and SQL Server never diverge. The typed settings service deserializes them into records; the rules engine snapshots rules.catalog and the rules.* thresholds into reconciliation_runs.thresholds_json on every run.

Roles (900) and core field maps (906) have real tables (roles, core_field_maps) and are seeded there.

  • The schema stays small: business tables plus the two library-owned sets (audit_log, qrtz_*).
  • Catalog edits are audited like every other settings change and take effect through the settings cache.
  • Bulk querying inside a catalog (for example “all rules of severity exception”) happens in memory, which is fine at these sizes (a few dozen entries each). If a catalog ever needs relational querying, it is promoted to a table with a new numbered script, never by editing a seed.
  • The same pattern later carried bank-owned configuration: rules.disabled and rules.custom (rules switched off by policy and bank-authored declarative rules, both recorded on each run) and extraction.templates (the template studio’s overlay, see ADR 0007).