Domain entity evaluation checklist

Table of Contents

Summary

A fully commissioned domain entity spans twelve layers: DB, codegen, domain, repository, service, messaging (JSON IO), Qt UI, shell REPL, CLI, HTTP routes, Wt web UI, and manual. Each layer has a fixed set of criteria that determines whether the entity is complete.

This checklist is not specific to ores.refdata. The criteria describe the standard pattern for any ORE Studio domain entity; the examples use ores.refdata/currency as the reference specimen because it is the most complete entity and was the first to be evaluated.

Two meta-types exist with different expected coverage:

  • Domain entity — a primary entity with its own full CRUD stack, Qt MDI window, shell and CLI commands, HTTP routes, and a manual chapter. Example: currency.
  • Auxiliary type — a small lookup table referenced by one or more domain entities as a soft-FK. Has the same DB, codegen, and service layers but a narrower UI surface: typically a dropdown in the parent entity's detail dialog rather than its own MDI workflow. Example: rounding_type, monetary_nature, currency_market_tier.

Each checklist table has columns: Item | Description | Domain | Aux | What to look for. Values in Domain/Aux: Always / Usually / If applicable / N/A.

Use alongside the Entity Coverage Matrix when commissioning or auditing any entity.

Detail

DB layer

Item Description Domain Aux What to look for
Table exists DDL file exists for the entity Always Always projects/ores.sql/create/refdata/refdata_<entity>s_create.sql
tenant_id column Multi-tenancy isolation Usually Always tenant_id uuid not null — omit only for truly global domain entities (currencies, countries)
version column Optimistic locking Always Always version integer not null
modified_by column Audit: who edited Always Always modified_by text not null
performed_by column Audit: which service performed it Always Always performed_by text not null; set by trigger to ores_iam_current_service_fn()
change_reason_code column Change tracking Always Always change_reason_code text not null
change_commentary column Change tracking Always Always change_commentary text not null
valid_from / valid_to Temporal history Always Always valid_from timestamp with time zone not null, valid_to timestamp with time zone not null
CHECK: temporal order Prevents invalid ranges Always Always check ("valid_from" < "valid_to")
CHECK: natural key not empty Data integrity Always Always check ("iso_code" <> '') or equivalent
GIST exclusion constraint Prevents temporal overlaps Always Always exclude using gist (tenant_id WITH =, <key> WITH =, tstzrange(valid_from, valid_to) WITH &&)
Primary key Uniqueness Always Always (tenant_id, <natural_key>, valid_from, valid_to)
Version unique index Fast lookup of current version Always Always UNIQUE (tenant_id, <key>, version) WHERE valid_to = ores_utility_infinity_timestamp_fn()
Natural key unique index Fast lookup of current record Always Always UNIQUE (tenant_id, <key>) WHERE valid_to = ores_utility_infinity_timestamp_fn()
Tenant index Tenant-scoped scans Always Always INDEX (tenant_id) WHERE valid_to = ores_utility_infinity_timestamp_fn()
display_order column Dropdown ordering N/A Always display_order integer not null default 0
description column Human-readable meaning of code N/A Always description text not null
Insert trigger Version management + validation Always Always create or replace function ores_refdata_<entity>s_insert_fn()
Insert trigger: security definer + set search_path Prevents search-path injection attacks Always Always Function header must carry security definer and set search_path = public, pg_temp. Without both, a service role with a manipulated search path can shadow ores_*_tbl references and bypass validation. See Commission: country § Analysis; rule is in PostgreSQL architecture § Insert trigger patterns.
Soft-delete rule History-preserving delete Always Always on delete to ... do instead update ... set valid_to = current_timestamp
Validation function Allows parent entities to validate soft-FK Always Always create or replace function ores_refdata_validate_<entity>_fn(p_tenant_id uuid, p_value text)
Validation function: security definer + set search_path Independently secure for direct-call use Always Always Validate functions must carry both attributes independently of their callers. security definer does not propagate from caller to callee. See Commission: country § Analysis.
Validation function: bootstrap checks active rows Bootstrap pass-through must use valid_to filter Always Always The not exists bootstrap guard must include and valid_to = ores_utility_infinity_timestamp_fn(). Without it, a table with only soft-deleted rows skips the pass-through and fails with a misleading "Must be one of: (empty list)" error. See Commission: country § Analysis.
Entity-specific soft-FK validations Trigger validates referenced lookup values Always N/A new.rounding_type : ores_refdata_validate_rounding_type_fn(…)=
party_id soft-reference Party-scoped entity If applicable N/A Domain only; check party_id uuid not null and trigger validation
workspace_id soft-reference Workspace-scoped entity If applicable N/A Domain only
coding_scheme_code column Optional DQ coding scheme link If applicable N/A coding_scheme_code text (nullable); validated in trigger if present

Codegen layer

Item Description Domain Aux What to look for
Domain entity model Codegen model for the C++ domain struct Always Always projects/ores.codegen/models/refdata/<entity>_domain_entity.json
Entity model Codegen model for the ODB entity + mapper Always Always projects/ores.codegen/models/refdata/<entity>_entity.json
Both models consistent with DB schema Model fields match DB columns Always Always Compare model field names/types against DDL columns
No hand-crafted divergence Generated files match what codegen would produce Always Always If a generated file has been edited manually, flag as drift risk

Domain layer (C++)

Item Description Domain Aux What to look for
Domain struct Plain-data C++ struct Always Always projects/ores.refdata.api/include/ores.refdata.api/domain/<entity>.hpp
All business columns represented No column missing from struct Always Always Compare struct fields against DDL columns (temporal managed by trigger, not struct)
display_order field Present for aux types N/A Always int display_order = 0;
description field Present for aux types N/A Always std::string description;
recorded_at or equivalent Exposes insert timestamp (maps to DB valid_from) Always Always std::chrono::system_clock::time_point recorded_at;
Canonical C++ types No raw pointers, no inheritance Always Always std::string, std::optional<T>, boost::uuids::uuid, int, bool
No logic in struct Plain data only Always Always No methods beyond constructors; no business logic

Repository layer (C++)

Item Description Domain Aux What to look for
Entity class ODB-annotated wrapper Always Always ores.refdata.core/include/.../repository/<entity>_entity.hpp
Mapper class DB row ↔ domain struct conversion Always Always repository/<entity>_mapper.hpp
Repository class Data access object Always Always repository/<entity>_repository.hpp
list, count, save, delete, get Core CRUD operations Always Always All five methods present in repository header
get_history Temporal history query Always Always Method returning all versions for a given key
list_for_party Party-scoped list If applicable N/A Only for party-scoped domain entities
Tenant-scoped queries Queries filtered by tenant_id Usually Always WHERE clause includes tenant_id condition

Service layer (C++)

Item Description Domain Aux What to look for
Service class Business logic + authorization wrapper Always Always ores.refdata.core/include/.../service/<entity>_service.hpp
list, count, save, delete, get, get_history Full CRUD + history Always Always All six methods present in service header
list_for_party Party-scoped list If applicable N/A Domain only
Authorization checks Caller permission validated Always Always Authorization call before data access
NATS event firing on mutation Events published on save and delete Always Always NATS publish calls in save and delete implementations

Messaging / JSON IO layer (C++)

Item Description Domain Aux What to look for
JSON struct rfl-based serialisation struct Always Always ores.refdata.api/include/.../domain/<entity>_json.hpp
IO handler domain ↔ JSON conversion Always Always domain/<entity>_json_io.hpp
Protocol header NATS message type definitions Always Always messaging/<entity>_protocol.hpp
Mutation message types Save/delete request + saved/deleted event Always Always save_<entity>, delete_<entity>, <entity>_saved, <entity>_deleted in protocol header
Query message types List, count, get, history Always Always list_<entity>s, count_<entity>s, get_<entity>, get_<entity>_history in protocol header

Qt layer

Item Description Domain Aux What to look for
MDI list window Top-level window in MDI area Always If applicable ores.qt.refdata/include/ores.qt/<Entity>MdiWindow.hpp
Detail dialog Single-record edit/view Always If applicable <Entity>DetailDialog.hpp
History dialog Temporal history for a record Always If applicable <Entity>HistoryDialog.hpp
Controller Wires windows to service Always If applicable <Entity>Controller.hpp
Client model Qt model/view data model Always If applicable Client<Entity>Model.hpp
Dropdown populated in parent dialog Aux type appears as a combo box choice N/A Always Check parent entity's DetailDialog.cpp for a combo box loading this type
Import dialog Bulk import support If applicable N/A Import<Entity>Dialog.hpp — not all entities have this
List window loads post-NATS Manual verification Always If applicable Open app, navigate to entity MDI window, records appear
Detail dialog edit + save round-trip Manual verification Always If applicable Edit a field, save, reopen — change persisted
History dialog shows correct history Manual verification Always If applicable After edit, history dialog shows two versions
Delete preserves history Manual verification Always If applicable Delete record; history dialog still shows prior versions
NATS eventing cross-session Manual verification Always If applicable Mutation in client A appears in client B without refresh

Shell layer (ores.shell interactive REPL)

Item Description Domain Aux What to look for
Submenu registered Entity has a dedicated submenu in the REPL Always If applicable projects/ores.shell/src/app/commands/<entity>s_commands.cpp registered in menu
list command Lists records from service Always If applicable list case in the commands file
add / save command Creates or updates a record Always If applicable add or save case
remove / delete command Deletes a record Always If applicable remove or delete case
history command Shows temporal history Always If applicable history case
Post-NATS service wiring Commands call service via NATS, not directly Always If applicable Service calls use NATS client, not direct repository

CLI layer (ores.cli non-interactive)

Item Description Domain Aux What to look for
Options config struct CLI argument struct for add/save Always If applicable projects/ores.cli/include/ores.cli/config/add_<entity>_options.hpp
list subcommand Non-interactive list Always If applicable Subcommand registered and implemented
add / save subcommand Non-interactive create/update Always If applicable Subcommand registered and implemented
remove / delete subcommand Non-interactive delete Always If applicable Subcommand registered and implemented
Post-NATS service wiring Commands call service via NATS Always If applicable Same as shell

HTTP layer

Item Description Domain Aux What to look for
GET list route REST list endpoint Always If applicable GET /refdata/<entity>s in routes file
POST save route REST create/update endpoint Always If applicable POST /refdata/<entity>s
DELETE batch route REST batch delete endpoint Always If applicable DELETE /refdata/<entity>s
GET history route REST history endpoint Always If applicable GET /refdata/<entity>s/:id/history
Routes registered Routes wired into the HTTP server Always If applicable Entry in projects/ores.http.core/src/routes/

Wt web UI layer

Item Description Domain Aux What to look for
List widget Wt list component Always If applicable projects/ores.wt.service/include/.../app/<entity>_list_widget.hpp
Detail dialog Wt edit component Always If applicable <entity>_dialog.hpp
Wired into Wt app Widget registered in application Always If applicable Reference in Wt app class
List + edit round-trip Manual verification Always If applicable Open Wt app, list entity, edit, save — change persisted

Manual

Item Description Domain Aux What to look for
Entity chapter Dedicated chapter in user guide Always If applicable doc/manual/user_guide/<entity>.org or equivalent
What and why Entity described for a user, not a developer Always If applicable First section: what this entity is, when to use it
Qt MDI window documented List window walkthrough Always If applicable Screenshot or description of columns and actions
Qt detail dialog documented Field-by-field documentation Always If applicable Table of fields with type, validation, and meaning
Qt history dialog documented History usage explained Always If applicable How to view and interpret change history
Shell commands documented All shell commands with examples Always If applicable #+begin_example blocks showing command + output
CLI commands documented All CLI subcommands with flags Always If applicable #+begin_example blocks showing invocation
Aux type documented in parent chapter Valid values listed and explained N/A Always Subsection in parent entity chapter: table of codes + descriptions
Indexed from manual root Chapter reachable from table of contents Always If applicable Entry in doc/manual/user_guide/index.org or equivalent
Site builds cleanly No broken links or missing files Always Always make site passes without error

See also

Emacs 29.1 (Org mode 9.6.6)