ADR 0003: Reference catalogs are seeded into `settings` as typed JSON, not into new tables
- Status: Accepted, 2026-08-28
Context
Section titled “Context”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.
Decision
Section titled “Decision”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.
Consequences
Section titled “Consequences”- 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.disabledandrules.custom(rules switched off by policy and bank-authored declarative rules, both recorded on each run) andextraction.templates(the template studio’s overlay, see ADR 0007).