Applied MASD

Table of Contents

Summary

This document is the applied counterpart to MASD. Where the knowledge docs describe the methodology as an observation of the external world, this one shows how ORE Studio instantiates each concept: how the logical space maps to authored entity models and the four-layer architecture, how the physical space maps to technical spaces, addresses, and Mustache templates, and how variability maps to the ores physical-space graph. ORE Studio ports MASD's ideas to LLMs and NLP rather than to a formal meta-meta-model: the underlying concepts are encoded in skills and recipes an LLM can understand and apply.

Detail

Logical space → entity models and the four-layer architecture

ORE Studio's logical entities are authored as literate org-mode model files under each component's modeling/ directory. Most reference-data entities are value types — immutable records identified by a code rather than an object identity (see the entity types in Logical Space).

ORE Studio does not expose a formal M3 meta-meta-model. Instead:

  • The implicit meta-model is the entity model file schema (.org authored files under modeling/).
  • MASD's meta-layer and meta-component concepts are physically instantiated as ORE Studio's four architectural layers and ores.* CMake targets.
  • Skills and recipes encode the meta-rules an LLM applies when authoring a new model — the equivalent of a formal meta-language in classical MDE.
MASD meta-concept ORE Studio instance (physical)
meta-layer a layer (e.g. the domain layer)
meta-component a component (e.g. ores.refdata.api)
meta-facet a facet (e.g. the domain facet of C++ TS)
meta-archetype an archetype (e.g. currency.hpp domain header)

The four layers (foundation / infrastructure / domain / application) and the components within them are therefore physical-space instances of the meta-layer and meta-component concepts. This is documented from the architecture perspective in System Model.

The entity model file as a unified literate document

The preceding section elided a question that deserves its own treatment: what, precisely, is one of these .org files? The honest answer is that it is not a single MASD artefact at all, but three artefacts that classical MDE keeps separate, deliberately collapsed into one physical document.

The three concerns a model file bundles

  1. The logical element itselfLogical Space's Structural package: the entity's primary key, natural keys, and columns. This is the entity's shape — its object graph. Adding a column changes it.
  2. Authored variability, at element scope — configuration that tunes how the (unchanged) shape above is projected into physical artefacts: naming, chrome, feature toggles. ORE Studio Variability Model catalogues these as authored feature bundles, applying Variability's test for telling this concern apart from the first: a property is variability only if the model author sets it and the generator merely reads it through; if the generator instead computes the value from the object graph (has_tenant_id, is_uuid, and the rest of that document's "Excluded: structural predicates" table), it is a consequence of concern (1), not a configuration choice, however boolean-shaped it looks in the property drawer.
  3. Protected regionsPhysical Space's partially automated mode of production: hand-written fragments — a bespoke query, a custom NATS message — merged into an otherwise fully generated file. ORE Studio calls its instance of this mechanism a paste block; see the Paste blocks page for the mechanics and its point of divergence from the textbook implementation (model-sourced and stateless, rather than round-tripped from the previous generated output).

None of these three is reducible to either of the others. A column addition (1) cannot be expressed as a variability feature (2) without violating the "variability is non-structural" discipline that makes (2) tractable at all; a custom query body (3) cannot be expressed as either, because no amount of declarative configuration produces arbitrary imperative code. Classical MDE therefore usually keeps them in separate artefacts — a class diagram for (1), a configuration model or profile for (2), round-trip markers scanned from generated output for (3) — synchronised by tooling. ORE Studio instead writes all three into one .org file, in adjacent sections of the same document.

Why collapse them — and the price of doing so

The justification is not that the distinction is false — the taxonomy above is exactly as real in ORE Studio as in any other MASD instantiation, and is the reason ORE Studio Variability Model can state a mechanical test for it at all. The justification is that co-location has a reader, and that reader is not only the generator. A literate document — in Knuth's original sense, prose and machine-consumed structure interleaved for a human audience — lets an engineer (or an LLM) see an entity's shape, its configured behaviour, and the one hand-written exception to its otherwise total regenerability, in a single pass, without cross-referencing a separate configuration file and a separate generated-output diff. ORE Studio Variability Model makes the same trade explicitly for concern (2) alone — "ORE Studio has no intermediate feature graph with inheritance and resolution… this keeps the system simple to maintain and to hand to an LLM, at the cost of the fine-grained, scoped per-feature resolution that a full VMM enables" — and the same calculus applies to bundling concerns (1)-(3) into one file: simplicity and legibility, purchased with the loss of MASD's clean separation of concerns as an enforced property of the toolchain. Nothing today stops an author writing a structural predicate into what should be a Behavioural knob's slot, or vice versa, except the discipline documented on the meta-model pages that describe each facet. The Codegen org-entity meta-model and its segment pages are, in this light, best read as the taxonomy that a formal VMM/LMM split would have enforced structurally, maintained instead as documentation discipline.

Contrast with UML

The nearest classical-MDE analogue is a UML class stereotyped and tagged with a Profile: the class itself is concern (1); its stereotype's tagged values are concern (2); and — where the tool supports it — a protected region inside the generated class body is concern (3). Seen this way, ORE Studio has not invented a new problem; it has re-solved an old one with different mechanics:

Concern UML + Profile mechanism ORE Studio mechanism
Logical shape The class's attributes and associations. The * Primary key=/=* Natural keys=/=* Columns sections.
Element-scope variability Tagged values on a UML Profile stereotype applied to the class. Property-drawer properties under * SQL=/=* C++ > ** Qt headings (feature bundles).
Protected regions Marker comments scanned from the previous generated file and reinserted (round-trip; loses content on an unstable marker ID). A :implements: babel-block property naming a kind UUID, read from the model, never the output (stateless; nothing to lose on regeneration — see Paste blocks).
Where the three concerns live Three loosely-coupled artefacts: the diagram, the profile application, the generated-and-edited file. One physical document, one org-roam node, one :ID:.

The UML mechanism keeps the three concerns in three places precisely because a diagramming tool, a profile-application mechanism, and a text editor are three different pieces of software with three different strengths; nothing in the concepts forces the separation. Org-mode, being plain text with a lightweight structure a parser can walk, removes the reason to keep them apart: a heading, a property drawer, and a babel block are all the same kind of thing to both a human reader and org_loader.py, so nothing is lost by writing all three concerns as headings in one file. The cost — no tool-enforced boundary between "this is structure" and "this is configuration" — is the same cost UML pays whenever an author puts a modelling decision in the wrong diagram; ORE Studio just pays it in one file instead of three.

Metatypes, facets, archetypes, and transforms

This section explains the core MASD vocabulary as it applies in ORE Studio, working from first principles with a concrete example at each step.

Entity

An entity is a concept in the logical model — something the domain cares about. Examples: currency, country, book. An entity is what a developer authors in an org-mode model file under modeling/. It is a logical object: it has fields and relationships, but no language, no file, no namespace yet.

Metatype

Every entity has a metatype — the classification of what kind of entity it is. The metatype is not a property of the entity in the domain sense; it is metadata about the entity that the code generator uses to decide which archetypes to apply.

Examples of metatypes in ORE Studio:

Metatype Meaning
domain_entity A full domain object: has fields, persists to a table, is exposed via a service. currency, country, book are all domain_entity.
junction An association entity joining two domain entities (no independent identity).
enumeration A closed, named set of values. book_status is an enumeration.
field_group A reusable cluster of fields shared across entities; produces no standalone artefacts.
component A CMake component descriptor; generates build scaffolding rather than domain artefacts.

The metatype is distinct from the entity's identity. currency is the entity; domain_entity is its metatype. Saying "currency is a type" is true in the programming-language sense but wrong in the MASD sense — in MASD, currency is an instance of the metatype domain_entity.

A note on UML stereotypes. In UML, a stereotype is the mechanism for attaching metadata to a model element — you annotate an element with <<value>> to indicate it is a value type. In ORE Studio we do not use UML or stereotypes: models are authored in org-mode and metadata is conveyed through the file's frontmatter (#+options, property drawers, and heading keywords). The concept we care about is the metatype; the mechanism we use is org-mode frontmatter. Do not use "stereotype" to mean "metatype" — they refer to different things.

Transform

A transform is a model-to-text operation that takes a logical entity and produces physical artefacts. In ORE Studio the transform engine is ores.codegen (projects/ores.codegen). It reads the entity's model file, resolves its metatype, selects the applicable archetypes, and renders each one to a file on disk. The act of running the generator for a given entity and address is one transform execution.

Facet

A facet is a named grouping of related archetypes within a technical space. Think of it as a package or namespace in physical space — not a subdivision of the technical space itself, but a classification of artefacts by the role they play for an entity.

Example: the domain facet groups every archetype that contributes to the domain representation of an entity in C++: the struct header, the JSON I/O header, the JSON I/O implementation. The repository facet groups the archetypes for persistence: entity, mapper, repository (header and implementation for each). These are distinct roles, so they are distinct facets.

Facets are defined as nodes in the ores physical-space graph (projects/ores.codegen/library/templates/).

Archetype

An archetype is a single template within a facet — the generating function for exactly one output file. Each archetype:

  • Belongs to exactly one facet.
  • Binds to one or more metatypes: it only fires when the entity's metatype matches. The domain::entity_header archetype binds to domain_entity; it does not apply to enumeration or junction.
  • Is parameterised by the logical model: the entity's name, fields, namespace, and relationships are substituted at render time.
  • Produces one output file — the artefact — per entity it is applied to.

Worked example: currency

Putting it together with a concrete case:

  1. currency is a logical entity with metatype domain_entity.
  2. A developer runs compass codegen generate --model ores.refdata.currency.org --address ores.cpp.domain.
  3. The transform (ores.codegen) reads the model, confirms the metatype is domain_entity.
  4. It resolves the domain facet from the ores.cpp.domain node in the physical-space graph.
  5. The domain facet contains three archetypes that bind to domain_entity:
    • domain::entity_headercurrency.hpp
    • domain::json_io_headercurrency_json_io.hpp
    • domain::json_io_implementationcurrency_json_io.cpp
  6. The transform renders each archetype against the model, producing three artefacts on disk.

If the metatype were enumeration instead, the domain facet would apply a different (smaller) set of archetypes — only those that bind to enumeration.

Address

An address is the ORE Studio selector: a physical-space path (ores.cpp.domain, ores.cpp, ores.sql.schema, …) that restricts a run to the facets under it. --address ores.cpp activates the domain, generator, repository, service, and protocol facets simultaneously — the whole C++ technical space in one invocation. Address is not a core MASD concept — MASD talks about facet activation through the variability model; ORE Studio surfaces this as the physical-space graph's own node paths, so there is no separate naming scheme to maintain (an entity's ores.* drawer binding and its --address selector are the same address vocabulary).

Terminology summary

Term Definition Example
Entity A logical model concept; something the domain cares about. currency, country
Metatype The classification of what kind of entity it is; a type of a type. domain_entity, enumeration
Facet A named grouping of related archetypes within a technical space; a package in physical space. domain, repository, sql
Archetype A template within a facet producing exactly one output file; binds to specific metatypes. domain::entity_headercurrency.hpp
Transform The model-to-text operation that applies archetypes to a logical entity to produce artefacts. Running ores.codegen
Artefact A concrete output file produced by an archetype for a specific entity. currency.hpp
Address The physical-space path selector: restricts a run to the facets under it. --address ores.cpp
Physical space binding Element-scope activation via ores.* property-drawer properties; overrides the entity's default supported set. :ores.cpp.qt.enabled: false
Technical space The language/platform ecosystem hosting the physical artefacts. C++, SQL, Qt

Terms to avoid and their replacements:

Avoid Use instead Why
type (for metatype) metatype A type is currency; the metatype classifies it as domain_entity.
stereotype metatype Stereotype is a UML mechanism; ORE Studio uses org-mode frontmatter.
layer facet (for a grouping) or technical space (for C++/SQL/Qt) "Layer" is informal and ambiguous.
tier metatype group Invented non-MASD word.

Physical space → technical spaces, addresses, and templates

ORE Studio's physical space is the concrete TS→Part→Facet→Archetype instantiation, indexed in ORE Studio Technical Spaces (with per-TS detail in C++ Technical Space, SQL Technical Space, and Other Technical Spaces). In the C++ TS the canonical parts are include/ and src/; the SQL TS has a single implicit part because its artefacts are not split by compile-time role.

ORE Studio has no compiled Physical Metamodel. The PMM's role is played by two artefacts:

  • The ores physical-space graph (projects/ores.codegen/library/templates/ores.*.org) — root → technical space → facet → archetype nodes naming every facet, its address, and its archetypes.
  • The Mustache template directory under projects/ores.codegen/library/templates/ — the archetype catalogue, where each .mustache file is one archetype, wrapped in a literate org document that tangles it and documents it.

Projections and the two codegen routes

ores.codegen is a Model-to-Text projector: it takes a model, applies Mustache archetypes selected by an address, and produces physical artefacts. It runs in two routes that share the same machinery but take their input from different sources:

Route Where the model comes from Format Where the model lives
Authored A human (or LLM) writes it. .org projects/ores.<group>/modeling/
Automated A script derives it from already-generated artefacts. .json build/output/codegen/ (gitignored)

The authored route is the common case: domain entities, lookup tables, junctions, and components are maintained by hand in literate org-mode, because the model carries intent other readers need (descriptions, custom-method prose, generator expressions). The org-mode migration story tracks moving the entire authored set off legacy JSON.

The automated route applies when the model is merely derived. The canonical example is the PlantUML ER diagram: a parse stage reads the generated SQL DDL and produces an intermediate JSON model; a render stage feeds it to a Mustache archetype and emits ores_schema.puml. That intermediate model is regenerated every run, carries nothing a human edits, and lives under build/output/ so it stays out of the input tree. projects/ores.codegen/models/ holds authored input only.

Address resolution: the physical-space graph and element-scope binding

ORE Studio does not maintain a formal VMM data structure for activation; instead the ores physical-space graph under projects/ores.codegen/library/templates/ plays that role: one node per technical space, fanning out to facet nodes, each carrying an * Archetypes table mapping every archetype to its output path. A facet's own address is the addressing unit — --address ores.cpp.qt selects exactly the ores.cpp.qt facet's templates directly, with no indirection through any intermediate name. A grouping that spans several facets is addressed one level up the hierarchy instead — a technical-space address (ores.cpp) for every C++ facet, the root address (ores) for everything.

The entity author's own choice of which facets apply — the supported set, \(S_e\), declared via ores.* properties on the entity's model — is the one part of this mechanism that is a genuinely authored feature; it is catalogued alongside the rest of the feature catalogue in ORE Studio Variability Model, including the specificity-resolution rule that governs how a partial ores.* binding is resolved into a full \(S_e\) (more-specific overrides less-specific, depth-first) — that rule is about authoring one entity's own \(S_e\) correctly, not about addressing mechanism, so it stays there rather than being restated here. Everything below this point is resolution mechanism a developer invokes, not a choice an entity author authors.

Model types — the scope filter

A model type is a tag that classifies each model file and controls which facet's archetypes can apply to it. A facet's model_types list acts as the filter: only a model whose type appears in that list is a valid target for the facet. Model type is derived from the filename suffix (JSON route) or the #+type: frontmatter field (org-mode route):

Model type Filename suffix Org frontmatter type What it represents
domain_entity *_domain_entity.json ores.codegen.entity Bi-temporal C++ entity with full SQL + C++ stack
schema *_entity.json ores.codegen.entity SQL-only schema entity (legacy JSON route)
junction *_junction.json ores.codegen.junction SQL junction (many-to-many) table
component ores.codegen.component Component scaffold (CMakeLists, stubs, export macros)
field_group ores.codegen.field_group Nested value struct (no DB mapping)
enum ores.codegen.lookup_entity C++ enum class with string conversion
service_registry ores.codegen.service_registry DB service registration (users, grants, accounts)
data PlantUML ER diagrams

This filter prevents, for instance, the component scaffold facet from inadvertently running against entity models, and the domain facet from running against SQL-only table models.

Invoking --address

Generation is scoped at invocation time via the CLI's --address:

./projects/ores.codegen/codegen.sh generate \
  --component refdata \
  --address ores.cpp.domain

Omitting --address generates the entity's full supported set; giving an address restricts the run to the facets under that physical-space subtree. Component scope is handled by src/codegen/manifest.py: the manifest maps component names to their model discovery roots (models_dir for legacy JSON models; modeling_dir for org-mode model files). It does not assign a default address — that is always either omitted (full supported set) or given explicitly by the developer, a skill, or a compass recipe. Product scope — activation applying across the whole product — is expressed the same way, by a technical-space or root address (ores.cpp, ores), and by skills that bundle common multi-facet invocations into a single command.

Target set and the generation set

The target set \(T\) is what to generate in this particular run. Default: \(T = S_e\), the entity's own supported set. The CLI --address argument overrides \(T\):

# T = S_e — generate everything the entity supports
./projects/ores.codegen/codegen.sh generate --component refdata

# T = all sql archetypes
./projects/ores.codegen/codegen.sh generate --component refdata --address sql

# T = all cpp.qt archetypes
./projects/ores.codegen/codegen.sh generate --component refdata --address cpp.qt

There is no --profile flag; the entity's ores.* binding IS the generation footprint, and --address is a filter on it, never an expansion. What generates for entity \(e\) is \(T \cap S_e\). When \(T \cap S_e = \emptyset\) the run emits a warning ("<entity>: nothing to generate for address <addr>") and continues; an --address value unknown to the facet catalogue is an error.

Technical-space to facet map

The TS→facet map lives in each technical space's own * Facets table in the ores physical-space graphores.cpp, ores.sql, and the other TS root nodes — which is the sole source of truth for which facets belong to which technical space; it is not reproduced here. All supported/target set logic lives in codegen/physical_space.py — isolated from dispatch code and covered by a dedicated test suite (tests/test_physical_space.py); this is deliberately complex logic and must not be diffused across multiple modules.

The facet catalogue

Each facet has its own physical-space address and is documented by a literate template org file.

C++ technical space

Facet Address Literate source Archetypes Purpose
Domain ores.cpp.domain ores.cpp.domain 3 Temporal domain struct and JSON I/O
Generator ores.cpp.generator ores.cpp.domain 2 Test-data generator (sample-value builder)
Repository ores.cpp.repository ores.cpp.repository 12 Entity, mapper, repository (temporal + non-temporal)
Messaging ores.cpp.protocol ores.cpp.protocol 6 NATS event, handler, protocol types, service
Service application ores.cpp.service-app ores.cpp.service-app 12 Application, host, config options/parser, entry point
Qt UI ores.cpp.qt ores.cpp.qt 10 MDI window, controller, detail/history dialogs, client model
Component ores.cpp.component ores.cpp.component 6 Umbrella header, export macros, test main, stubs
Model types ores.cpp.enum ores.cpp.enum 2 Enum class, field-group struct
Table part of ores.cpp.domain ores.cpp.domain 4 libfort table adapter and I/O

Per-facet references: Type definition facet, Entity lifecycle (repository and service), Service application facet, ORE Studio Messaging Reference (protocol), Qt facet.

SQL technical space

Facet Address Literate source Archetypes Purpose
Schema ores.sql.schema ores.sql.schema 9 DDL create/drop, notify trigger, junction tables
Service ores.sql.service ores.sql.service 4 Service users, DB grants, IAM accounts and roles
Populate ores.sql.populate ores.sql.populate 13 Reference-data seed scripts

See SQL facet.

CMake, Assets, and Doc technical spaces

Facet Address Literate source Archetypes Purpose
CMake build ores.cmake.component ores.cmake.component 8 Component root, src variants, tests, modeling target
Assets ores.cpp.qt ores.cpp.qt 4 Qt Designer UI forms, PlantUML ER diagram, shell vars
Documentation ores.doc ores.doc 20 Agile and knowledge docs, skills, recipes, runbooks

See Build facet, Assets facet, Documentation facet.

Manually authored facets

These facets are authored rather than generated, and documented for parity: Shell facet, CLI facet, HTTP facet, Wt facet.

The key attributes of a logical element live in the domain facet — regular C++ defining the domain struct. Each element lives on its native component (e.g. account in ores.iam) and is created via the Domain Type Creator skill, which bundles several facets together; other facets refer to the domain type files to extract the information they need.

The currency reference implementation and backout strategy

ORE Studio applies MASD's reference-implementation backout strategy directly: rather than designing an abstract generator first, it extracts the generator from a proven hand-written entity.

  1. Commission currency by hand — every layer written without generator involvement. currency is the reference implementation.
  2. Identify the SRPPs — the entity evaluation checklist formalises what "fully commissioned" means at each layer; patterns identical across all checklist items are extraction candidates.
  3. Encode each SRPP as a Mustache archetype parameterised by the entity model.
  4. Register the archetypes as rows in the facet's Archetypes table in the ores.* physical-space graph.
  5. Verify zero drift — run the generator against the currency model and diff against the hand-written files; a zero diff confirms faithful extraction.
  6. Extend to new entities — commissioning a new entity reduces to authoring its model and running the generator.

Commissioning currency produced the canonical SRPP catalogue for reference-data entities: the C++ domain struct with rfl-based JSON I/O, the repository entity/mapper/CRUD trilogy, the NATS service and protocol types, the SQL DDL and trigger scripts, and the full Qt UI (10 C++ archetypes plus 2 .ui archetypes in the Assets TS).

Automation spectrum

ORE Studio's position on the MASD methodology's automation spectrum:

Level Description ORE Studio status
0 Stub generation only Exceeded
1 One layer generated (e.g. SQL only) Exceeded
2 Multiple layers generated; some manual editing Achieved for SQL, domain
3 Full entity generated with no manual editing Partial (SQL + domain + protocol)
4 Product-line generation: new entity = new model Not yet reached

The immediate goal is Level 3 for the complete entity stack, using currency as the fitness function.

The six principles, applied

  1. Focus Narrowly — the generator produces mechanical layers (C++ plumbing, SQL DDL, Qt boilerplate) but never domain logic inside a service class.
  2. Integrate Pervasivelyores.codegen is invoked via compass, emits files that slot into CMake targets and the existing layout, and carries the standard licence header and editor modeline.
  3. Evolve Gradually — each new facet is added when a second real entity needs it; commissioning surfaces SRPPs, which become archetypes, which become facets.
  4. Govern Openly — all templates, models, and the physical-space graph sit in the open repo under the same review as production code.
  5. Standardise Judiciously — Mustache, JSON, and Python at the core; artefacts conform to project conventions.
  6. Assist and Guide — the automation spectrum above; ORE Studio currently operates between Level 2 and Level 3.

See also

MASD methodology (knowledge)

ORE Studio physical and system model

ORE Studio tooling

Emacs 29.3 (Org mode 9.6.15)