Applied MASD
Table of Contents
- Summary
- Detail
- Logical space → entity models and the four-layer architecture
- The entity model file as a unified literate document
- Metatypes, facets, archetypes, and transforms
- Physical space → technical spaces, addresses, and templates
- Projections and the two codegen routes
- Address resolution: the physical-space graph and element-scope binding
- The facet catalogue
- The currency reference implementation and backout strategy
- Automation spectrum
- The six principles, applied
- See also
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 (
.orgauthored files undermodeling/). - 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
- The logical element itself — Logical 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.
- 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. - Protected regions — Physical 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_headerarchetype binds todomain_entity; it does not apply toenumerationorjunction. - 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:
currencyis a logical entity with metatypedomain_entity.- A developer runs
compass codegen generate --model ores.refdata.currency.org --address ores.cpp.domain. - The transform (
ores.codegen) reads the model, confirms the metatype isdomain_entity. - It resolves the
domainfacet from theores.cpp.domainnode in the physical-space graph. - The
domainfacet contains three archetypes that bind todomain_entity:domain::entity_header→currency.hppdomain::json_io_header→currency_json_io.hppdomain::json_io_implementation→currency_json_io.cpp
- 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_header → currency.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.mustachefile 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 graph — ores.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 |
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.
- Commission
currencyby hand — every layer written without generator involvement.currencyis the reference implementation. - Identify the SRPPs — the entity evaluation checklist formalises what "fully commissioned" means at each layer; patterns identical across all checklist items are extraction candidates.
- Encode each SRPP as a Mustache archetype parameterised by the entity model.
- Register the archetypes as rows in the facet's Archetypes table in
the
ores.*physical-space graph. - Verify zero drift — run the generator against the
currencymodel and diff against the hand-written files; a zero diff confirms faithful extraction. - 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
- Focus Narrowly — the generator produces mechanical layers (C++ plumbing, SQL DDL, Qt boilerplate) but never domain logic inside a service class.
- Integrate Pervasively —
ores.codegenis invoked viacompass, emits files that slot into CMake targets and the existing layout, and carries the standard licence header and editor modeline. - Evolve Gradually — each new facet is added when a second real entity needs it; commissioning surfaces SRPPs, which become archetypes, which become facets.
- Govern Openly — all templates, models, and the physical-space graph sit in the open repo under the same review as production code.
- Standardise Judiciously — Mustache, JSON, and Python at the core; artefacts conform to project conventions.
- Assist and Guide — the automation spectrum above; ORE Studio currently operates between Level 2 and Level 3.
See also
MASD methodology (knowledge)
- MASD — the methodology and conceptual model this document applies.
- Logical Space, Physical Space, Technical Space, Facet, Variability — the individual concept docs.
ORE Studio physical and system model
- ORE Studio Technical Spaces — the concrete TS→Part→Facet→Archetype model.
- ORE Studio Variability Model — the ores physical-space graph, model types, activation.
- System Model — the four-layer architecture as physical-space instances.
- Component architecture — API/core/service split across components.
- Entity lifecycle — layer ordering and conventions for a full-stack entity.
ORE Studio tooling
- ores.codegen — the generator; inputs, templates, addresses, recipes.
- ores.codegen architecture — directory layout and template system.
- ores.refdata entity evaluation checklist — the "fully commissioned" fitness function.
- Domain Type Creator — the skill that generates all facets of an entity.