Codegen org-entity meta-model
Table of Contents
- About this document ignore
- File-level conventions
- Sections
- Block kinds
- SQL: extra drop statements
- Repository: additional class-member declarations
- Repository: additional out-of-class implementations
- Repository: additional .cpp includes
- Service header: extra includes
- Service header: custom public methods
- Service header: extra private members
- Service .cpp: extra includes
- Service .cpp: constructor extra init
- Service .cpp: custom implementations
- Generator header: custom declarations
- Generator .cpp: custom implementations
- Notes
A codegen entity model is a single literate org-mode file that describes
one entity (e.g. party) and drives both the C++ and SQL code generation
pipelines for that entity.
This document is the single source of truth for the file shape: every section, every required and optional property, every well-known block kind that templates may reference.
About this document ignore
The :ignore: tag asks the codegen loader to skip this heading when reading
an entity file. Sections without that tag may be consumed.
File-level conventions
Naming
Entity files live under projects/ores.codegen/models/<component>/ with the
filename <entity>_entity.org. Example: party_entity.org under
models/refdata.
Frontmatter
The first few lines of the file are org-mode frontmatter. They describe the document (this codegen model), not the modelled entity. Required:
| Keyword | Purpose |
|---|---|
:ID: (drawer) |
UUID of this org file. Used for cross-document links. |
#+title: |
Fully-qualified entity name, e.g. ores.refdata.party. |
#+description: |
What this file is, not what the modelled thing is. |
#+type: |
ores.codegen.entity |
#+component: |
Component the entity belongs to (refdata, trading, …). |
#+filetags: |
:model:entity:<component>: |
#+entity_singular: |
Identifier form, singular (party). |
#+entity_plural: |
Identifier form, plural (parties). |
#+entity_title: |
Title-cased human form (Party). |
#+brief: |
One-line summary of the modelled entity. |
Prose body (between frontmatter and first heading)
A paragraph or two describing the modelled entity itself. This text is
exposed to templates as the entity description. Keep the document-level
#+description for the file and put the entity description here.
Sections
The body is divided into top-level sections. The loader recognises the
section names below. Unrecognised section names (e.g. * About this file,
typically tagged :ignore:) are skipped.
Flags
Cross-cutting properties consumed by both C++ and SQL codegen.
| Property | Required | Notes |
|---|---|---|
:schema: |
yes | Postgres schema (almost always public). |
:product: |
yes | Top-level namespace (ores). |
:component: |
yes | Component name (refdata, trading, …). |
:has_tenant_id: |
no | Adds tenant_id column / field. |
:has_workspace_id: |
no | Adds workspace_id column / field. |
Example:
* Flags
,:PROPERTIES:
,:schema: public
,:product: ores
,:component: refdata
,:has_tenant_id: true
,:END:
Primary key
Drawer holds the primary-key column description. Body holds prose documentation.
| Property | Required | Notes |
|---|---|---|
:column: |
yes | SQL column name. |
:type: |
yes | SQL type (uuid, text, etc.). |
:cpp_type: |
yes | C++ type (boost::uuids::uuid, std::string, …). |
Example:
* Primary key
,:PROPERTIES:
,:column: id
,:type: uuid
,:cpp_type: boost::uuids::uuid
,:END:
UUID uniquely identifying this party.
Surrogate key for the party record.
Natural keys
Container for natural-key sub-headings. Each ** sub-heading is one
natural key, with the column name as the heading title.
Each natural-key sub-heading uses the same property structure as a column
(see * Columns below), and may carry a generator babel block.
Columns
Container for column sub-headings. Each ** sub-heading is one column,
with the column name as the heading title.
Per-column properties:
| Property | Required | Notes |
|---|---|---|
:type: |
yes | SQL type. |
:cpp_type: |
yes | C++ type. |
:nullable: |
no | true for nullable columns (default false). |
Each column's body is split into a description (first paragraph) and an optional detail (second paragraph and beyond).
Each column may carry one or more named babel blocks. The well-known names:
generator— C++ expression producing a synthetic value for tests.
Example:
** codename ,:PROPERTIES: ,:type: text ,:cpp_type: std::string ,:nullable: false ,:END: Globally unique human-readable codename (adjective_noun). Used as the per-party pgmq queue prefix, pg_cron job names, and operator tooling. #+begin_src cpp :name generator ,std::string(faker::word::adjective()) + "_" + std::string(faker::word::noun()) #+end_src
SQL
SQL-specific knobs. The first sub-section is always ** Flags with a
property drawer.
* SQL ** Flags ,:PROPERTIES: ,:tablename: ores_refdata_parties_tbl ,:END:
Future SQL sub-sections (custom triggers, indices) will live alongside the
Flags sub-heading.
C++
C++-specific knobs. Sub-sections:
** Flags— Repository / service flags that affect template output.** Repository— Naming-convention strings (entity_singular_short,entity_plural_short, etc.).** Domain includes— Namedincludesbabel block; one#includedirective per line.** Entity includes— Same, for the SQL-mapping entity header.** Conventions— Misc C++ properties (iterator_var, etc.).** Table display— Org table mapping column names to display headers.** Qt— Property drawer plus sub-tables (*** Detail fields,*** Columns (Qt model)).** Custom repository methods— Custom methods that the standard repository template cannot express. See "Block kinds" below.
Block kinds
A block kind is a slot in a template that an entity model can fill.
Each kind has a stable UUID; templates reference the slot by that UUID;
entity models attach blocks to the slot by listing the kind UUID in the
block's :implements: property.
Two levels of identity are at play:
- The kind UUID is well-known, lives here in the meta-model, and is referenced by templates.
- An instance UUID (each individual block's
:ID:) is per-entity, per block. Many entities may implement the same kind.
SQL: extra drop statements
Additional DROP statements to be inserted in the generated
<component>_<entity_plural>_drop.sql just before drop table if exists.
Use this when the entity's companion _table.org model generates database
objects (e.g. a validate function) that the entity template cannot know about.
If no entity implements this kind, the marker collapses to nothing.
Template usage (mustache / sql_schema_domain_entity_drop.mustache):
drop function if exists {{product}}_{{component}}_{{entity_plural}}_insert_fn;
<<paste:5E47F108-1350-4540-B3C2-E83DD5379B2D>>
drop table if exists "{{product}}_{{component}}_{{entity_plural}}_tbl";
Entity attachment (** SQL > ** Extra drops sub-section):
** Extra drops #+begin_src sql :name body :implements 5E47F108-1350-4540-B3C2-E83DD5379B2D drop function if exists ores_refdata_validate_rounding_type_fn; #+end_src
Multiple drop statements can appear in one block or in multiple blocks;
codegen concatenates them in order with a blank line between blocks.
Repository: additional class-member declarations
Class-member declarations to be inserted into the generated
<entity>_repository.hpp just before the closing brace of the
<entity>_repository class.
Template usage (mustache):
, void remove(context ctx, const std::string& id); , // <<paste:DCA78C69-E508-48D9-9972-A9B8094D91FB>> ,};
Entity attachment (per custom method):
*** read_system_party ,:PROPERTIES: ,:ID: 9B7C5D2E-1A4F-4E7B-9D8A-6F3C2E1B5A4D ,:END: Prose describing what this method does and why it cannot be templated. #+begin_src cpp :name declaration :implements DCA78C69-E508-48D9-9972-A9B8094D91FB ,std::vector<domain::party> ,read_system_party(context ctx, const std::string& tenant_id); #+end_src
The :implements header argument on the babel block is the contract:
codegen substitutes the <<paste:DCA78C69-...>> marker with the
concatenation of every babel block in the current entity whose
:implements header argument matches.
The babel block's :name is for Emacs / human authoring purposes (so
authors can navigate by name); :implements is what codegen looks at.
Repository: additional out-of-class implementations
Out-of-class method definitions to be inserted into the generated
<entity>_repository.cpp at the marker, typically just before the closing
namespace brace.
Templates reference: <<paste:F2EB1914-5E94-42CA-9C77-46BDB364BF9E>>.
Entity attachment: any babel block with
:implements F2EB1914-5E94-42CA-9C77-46BDB364BF9E contributes its body
to the substitution.
Repository: additional .cpp includes
Additional #include directives that the implementations above need.
Inserted into the generated <entity>_repository.cpp near the top of the
include block.
Templates reference: <<paste:6141050C-0ED4-4680-B387-7DDDA3A69806>>.
Entity attachment: any babel block with
:implements 6141050C-0ED4-4680-B387-7DDDA3A69806 contributes its body
(verbatim, no syntax stripping). Multiple methods may each declare their
own includes block; the codegen concatenates them. The C++ compiler
de-duplicates #include directives at preprocessing time anyway.
Service header: extra includes
Extra #include directives injected into the generated
<entity>_service.hpp immediately before the namespace opening brace.
Use this when the service needs headers for entity-specific types (e.g.
<boost/uuid/uuid.hpp>, a junction repository header) that the generic
service template does not emit.
Templates reference: <<paste:C5F35C7E-6EED-48D8-99F8-793145586882>>.
Entity attachment example:
** Custom service includes #+begin_src cpp :name includes :implements C5F35C7E-6EED-48D8-99F8-793145586882 ,#include <boost/uuid/uuid.hpp> ,#include "ores.refdata.core/repository/party_country_repository.hpp" #+end_src
Service header: custom public methods
Additional public method declarations injected into the generated
<entity>_service.hpp just before the private: label.
Use this for entity-specific query methods that are beyond the standard CRUD surface (e.g. party-scoped list/count operations on junction tables).
Templates reference: <<paste:2D4EA0F9-6AF6-45A5-A959-F672BF866C6A>>.
Entity attachment example:
** Custom service methods #+begin_src cpp :name declaration :implements 2D4EA0F9-6AF6-45A5-A959-F672BF866C6A , std::vector<domain::country> list_countries_for_party( , const boost::uuids::uuid& party_id, , std::uint32_t offset, std::uint32_t limit); , std::uint32_t count_countries_for_party(const boost::uuids::uuid& party_id); #+end_src
Service header: extra private members
Additional private member declarations injected into the generated
<entity>_service.hpp after the standard repo_ member.
Use this when the service needs entity-specific members such as a junction repository that the generic template does not know about.
Templates reference: <<paste:047A96EF-1CC4-4661-B80B-C6C4532076AB>>.
Entity attachment example:
** Custom service members #+begin_src cpp :name declaration :implements 047A96EF-1CC4-4661-B80B-C6C4532076AB , repository::party_country_repository junction_repo_; #+end_src
Service .cpp: extra includes
Additional #include directives injected into the generated
<entity>_service.cpp after the standard includes (<cstdint>,
<stdexcept>).
Use this for headers that are needed only by the custom method
implementations injected via D3E7A5F2 (e.g. <algorithm>,
<unordered_set>, <boost/uuid/uuid_io.hpp>) — headers that are
implementation details and should not appear in the service header.
Templates reference: <<paste:A7D3F1E2-8C5B-4A2F-B9E0-3D6C1A8F4B2E>>.
Service .cpp: constructor extra init
Additional member-initialiser-list entries injected into the generated
<entity>_service.cpp constructor, appended after ctx_(std::move(ctx)).
Use this when extra private members (declared via 047A96EF) need
initialisation with ctx_. Each block should start with , to extend the
initialiser list correctly.
Templates reference: <<paste:4F8A2B1E-7C3D-4E9F-B6A0-1D5C8F2E7A4B>>.
Entity attachment example:
** Custom service constructor init #+begin_src cpp :name ctor_init :implements 4F8A2B1E-7C3D-4E9F-B6A0-1D5C8F2E7A4B , , junction_repo_(ctx_) #+end_src
Service .cpp: custom implementations
Out-of-class method definitions injected into the generated
<entity>_service.cpp just before the closing namespace brace.
Use this for the implementations of methods declared via 2D4EA0F9 — the
standard template only generates bodies for the built-in CRUD methods;
entity-specific methods must supply their own bodies here.
Templates reference: <<paste:D3E7A5F2-8B4C-4D1E-9A6F-2E1C7B4D8A3F>>.
Entity attachment: any babel block with
:implements D3E7A5F2-8B4C-4D1E-9A6F-2E1C7B4D8A3F contributes its body.
Multiple blocks are concatenated with a blank line between them.
Generator header: custom declarations
Additional free-function declarations injected into the generated
<entity>_generator.hpp after the standard synthetic generator
declarations, before the closing namespace brace.
Use this for entity-specific generator functions not expressible through
the standard generate_synthetic_<entity> template (e.g. fictional/
hardcoded generators for test entities with authoritative data sets).
Templates reference: <<paste:E32E358B-56EC-4642-911E-A2EA1D95E3B4>>.
Entity attachment example:
** Custom generator declarations #+begin_src cpp :name declaration :implements E32E358B-56EC-4642-911E-A2EA1D95E3B4 ,ORES_REFDATA_API_EXPORT std::vector<domain::country> ,generate_fictional_countries(std::size_t n, , utility::generation::generation_context& ctx); #+end_src
Generator .cpp: custom implementations
Out-of-class function definitions injected into the generated
<entity>_generator.cpp after the standard generate_synthetic_<plural>
function, before the closing namespace brace.
Use this for the implementations of functions declared via E32E358B.
Templates reference: <<paste:C2F8A4E3-7D5B-4C2F-8A1E-3D9C6B2E5F1A>>.
Entity attachment: any babel block with
:implements C2F8A4E3-7D5B-4C2F-8A1E-3D9C6B2E5F1A contributes its body.
Notes
Why two levels of UUID
The kind UUID is a concept (akin to an interface), declared once in the meta-model. The instance UUID identifies an implementation (akin to a class) — there will be many. Templates speak the language of concepts; entity models speak the language of implementations.
This also means we never have to coordinate UUID changes across templates and per-entity files: a template references a fixed kind UUID; entity files discover what they implement.
Template marker syntax
Templates use <<paste:UUID>> as the marker. Codegen is a post-rendering
substitution pass: mustache renders first, then a regex pass replaces every
<<paste:UUID>> with the concatenated body blocks of every implementing
sub-heading in the current entity.
If no entity sub-heading implements a given kind, the marker is replaced with an empty string (and a single newline is collapsed).
Block-name conventions
Inside an implementing sub-heading the well-known babel block name is
body. Future kinds may use additional names (declaration,
implementation, etc.) — they will be documented alongside each kind.
Future work
- Stage 1.5: tag-based heading dispatch. Replace title matching ("Flags",
"Primary key", …) with tag matching (
:section:flags:, etc.). Lets us rename headings without breaking codegen. - Migration of remaining ~100 entity models from JSON to org.
- Compass scaffold for
compass add entity-org.