Codegen entity meta-model — keys and columns

Table of Contents

This page is a segment of the Codegen org-entity meta-model hub. It is the one segment that does not map to a single facet — a column declaration is relevant to both SQL (to generate the table's DDL) and C++ Domain/C++ Repository (to generate the domain struct field and the sqlgen-mapped entity field), so it was abstracted into a segment of its own rather than duplicated in each. Author a column once here; it becomes a SQL column, a domain struct field, and a repository entity field, all three kept in sync by construction.

This page splits its fields across two diagrams, per Applied MASD § "The entity model file as a unified literate document":

Physical model mapping

This segment projects into three facets in physical space:

Facet Description
ores.sql.schema Bi-temporal schema: table create/drop, insert triggers, notify triggers.
ores.cpp.domain Domain types: class, JSON I/O, table I/O.
ores.cpp.repository Repository layer: entity, mapper, CRUD.

Flags

Cross-cutting properties consulted by both the SQL and C++ pipelines, set once at the top of the file:

Property Required Meaning
:schema: yes Postgres schema (almost always public).
:product: yes Top-level C++ namespace (ores).
:component: yes Component name (refdata, trading, …) — drives both the C++ namespace and the <product>_<component>_... SQL naming convention.
:has_tenant_id: no See Multi-tenancy below.
:has_workspace_id: no See Multi-tenancy below.

Structural view — the identity properties above, plus the Primary key, Natural key, and Column property sets documented further down this page:

entity_meta_model_keys_and_columns.png

(Source: entity_meta_model_keys_and_columns.puml, rendered with plantuml.)

* Flags
,:PROPERTIES:
,:schema: public
,:product: ores
,:component: refdata
,:has_tenant_id: true
,:END:

Multi-tenancy: has_tenant_id=/=has_workspace_id

Setting :has_tenant_id: true tells the generator to emit a tenant_id uuid not null column on the table and a matching field on the domain struct, isolating the entity's rows by tenant; omit it only for genuinely global entities (currencies, countries). :has_workspace_id: does the same for workspace-scoped isolation.

Non-structural view — neither property is itself a field of the entity: authoring :has_tenant_id: true produces no field named has_tenant_id anywhere in the generated code. Rather, the generator reads it to decide whether to branch into the template logic that emits the tenant_id column and field. That the resulting column exists is a structural fact about the entity once the decision is made; the decision itself is a genuine, authored VMM feature, no different in kind from SQL's :has_parent_id::

entity_meta_model_keys_and_columns.svg

Primary key

The entity's surrogate key. In SQL this becomes part of the composite primary key (paired with tenant_id and the temporal range — see SQL); in C++ it becomes an sqlgen::PrimaryKey<T>-wrapped field on the repository entity (not on the domain struct — the domain struct exposes it as a plain field, the wrapping is a repository-layer concern).

Property Required Meaning
:column: yes SQL column name.
:type: yes SQL type (uuid, text, etc.).
:cpp_type: yes C++ type (boost::uuids::uuid, std::string, …).

Example of book's primary key:

* Primary key
,:PROPERTIES:
,:column:   id
,:type:     uuid
,:cpp_type: boost::uuids::uuid
,:END:

UUID uniquely identifying this book.

Generates, in the repository entity (book_entity.hpp):

struct book_entity {
    constexpr static const char* schema = "public";
    constexpr static const char* tablename = "ores_refdata_books_tbl";

    sqlgen::PrimaryKey<std::string> id;
    // ...
};

(book's primary key is actually text, not uuid — see C++ Qt's :has_uuid_primary_key: knob, which governs how the Qt layer treats whichever type is declared here; the SQL/repository/domain layers just use whatever :type:=/:cpp_type:= says, uuid or not.)

Natural keys

A container for natural-key sub-headings — the human-meaningful column(s) that uniquely identify a record within a tenant (as opposed to the surrogate Primary key above, which exists purely for foreign-key stability). Each ** sub-heading is one natural key, using the same property shape as a column (see below), and may carry a generator babel block for synthetic test data.

Columns

A container for the entity's remaining column sub-headings. Each ** sub-heading is one column, its heading title the column name.

Property Required Meaning
: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 — becomes the field's doc-comment) and an optional detail (second paragraph onward — extra context for a human reader, not emitted into generated code). A column may also carry a generator babel block: a C++ expression producing a synthetic value for that field, used by the C++ Generator facet.

Example of book's name column:

** name
,:PROPERTIES:
,:type:     text
,:cpp_type: std::string
,:nullable: false
,:END:

Human-readable identifier for this book.

Unique per tenant; used as the natural key in the Qt list window.

#+begin_src cpp :name generator
,std::string(faker::word::adjective()) + "_" + std::string(faker::word::noun())
#+end_src

Generates, in the domain struct (book.hpp):

namespace ores::refdata::domain {

struct book {
    // ...
    std::string name;
    // ...
};

}

And, in the SQL table DDL (refdata_books_create.sql):

create table if not exists "ores_refdata_books_tbl" (
    "id" uuid not null,
    "tenant_id" uuid not null,
    "version" integer not null,
    "party_id" uuid not null,
    "name" text not null,
    "description" text null,
    -- ...
);

See also

Emacs 29.3 (Org mode 9.6.15)