Documentation

Examples

The easiest way to understand a modelling language is to see how progressively richer models are built.

These examples begin with simple standalone concepts and proceed toward reusable, versioned model ecosystems. Each links to the section of the specification that defines the mechanism it uses.

Fundamentals

A single class, then the type and constraint vocabulary used to describe what its values may be.

01

A minimal class

Every OOML artefact states the specification version it targets, its own fully qualified name, and a human-readable name. Everything else is optional.

{  "ooml": "0.1.0",  "fqn": "com.example.crm/Customer@1.0.0",  "name": "Customer",  "description": "An organisation or person that buys goods or services.",  "attributes": {    "customerNumber": {      "kind": "primitive",      "type": "string",      "name": "Customer Number",      "required": true,      "description": "The identifier this customer is known by commercially."    }  }}
02

Types and constraints

Constraints describe which values are valid. They are logical statements about the value set, not instructions about storage.

{  "ooml": "0.1.0",  "fqn": "com.example.crm/Customer@1.1.0",  "name": "Customer",  "attributes": {    "customerNumber": {      "kind": "primitive", "type": "string",      "name": "Customer Number",      "required": true,      "pattern": "^CUST-[0-9]{6}$"    },    "creditLimit": {      "kind": "primitive", "type": "decimal",      "name": "Credit Limit",      "precision": 12,      "scale": 2,      "minimum": 0    },    "employeeCount": {      "kind": "primitive", "type": "uint32",      "name": "Employee Count",      "description": "uint32 states the permitted numeric range, not a storage width."    },    "onboardedAt": {      "kind": "primitive", "type": "datetime",      "name": "Onboarded At",      "required": true    },    "website": {      "kind": "primitive", "type": "uri",      "name": "Website",      "nullable": true    }  }}
Structure and relationships

How one class relates to another — by reference, by embedded structure, or by collection.

03

References and embedded structures

An object attribute points at an instance of another class by identity. A nested attribute describes an ad hoc structure inline, for a shape that has no meaning outside this class.

{  "ooml": "0.1.0",  "fqn": "com.example.hr/Employee@1.2.0",  "name": "Employee",  "attributes": {    "department": {      "kind": "object",      "type": "com.example.hr/Department@^1.0.0",      "name": "Department",      "required": true,      "description": "A reference to a Department instance, by identity."    },    "manager": {      "kind": "object",      "type": "self",      "name": "Manager",      "nullable": true,      "description": "'self' means the declaring class; tooling expands it."    },    "homeAddress": {      "kind": "nested",      "name": "Home Address",      "required": true,      "description": "No independent identity; described inline.",      "attributes": {        "streetName": {          "kind": "primitive", "type": "string",          "name": "Street Name", "required": true        },        "houseNumber": {          "kind": "primitive", "type": "string",          "name": "House Number", "required": true        },        "postalCode": {          "kind": "primitive", "type": "string",          "name": "Postal Code", "required": true        }      }    }  }}
04

Collections

Lists are ordered and permit duplicates; sets do not permit duplicates; maps hold keyed entries. A collection's values may themselves be nested structures.

{  "ooml": "0.1.0",  "fqn": "com.example.hr/Employee@1.3.0",  "name": "Employee",  "attributes": {    "phoneNumbers": {      "kind": "list",      "valueKind": "primitive",      "valueType": "string",      "name": "Phone Numbers",      "maxItems": 10    },    "roles": {      "kind": "set",      "valueKind": "object",      "valueType": "com.example.hr/Role@^1.0.0",      "name": "Roles",      "required": true,      "minItems": 1    },    "localizedTitles": {      "kind": "map",      "valueKind": "primitive",      "valueType": "string",      "name": "Localized Titles",      "description": "Keys default to primitive strings — here, ISO 639-1 language codes."    },    "previousAddresses": {      "kind": "set",      "valueKind": "nested",      "valueType": {        "streetName": {          "kind": "primitive", "type": "string",          "name": "Street Name", "required": true        },        "movedOut": {          "kind": "primitive", "type": "date",          "name": "Moved Out", "required": true        }      },      "name": "Previous Addresses"    }  }}
05

Inheritance

A subclass is a more specific form of its superclass and inherits everything defined above it. Inheritance states that one thing is a kind of another.

{  "ooml": "0.1.0",  "fqn": "com.example.hr/Party@1.1.0",  "name": "Party",  "description": "Anything the organisation can hold a relationship with.",  "abstract": true,  "attributes": {    "id": {      "kind": "primitive", "type": "uuid",      "name": "Identifier", "required": true    }  }}{  "ooml": "0.1.0",  "fqn": "com.example.hr/Person@1.0.0",  "name": "Person",  "abstract": true,  "extends": ["com.example.hr/Party@^1.1.0"],  "attributes": {    "firstName": {      "kind": "primitive", "type": "string",      "name": "First Name", "required": true    },    "lastName": {      "kind": "primitive", "type": "string",      "name": "Last Name", "required": true    }  }}{  "ooml": "0.1.0",  "fqn": "com.example.hr/Employee@1.0.0",  "name": "Employee",  "extends": ["com.example.hr/Person@^1.0.0"],  "attributes": {    "employeeNumber": {      "kind": "primitive", "type": "string",      "name": "Employee Number", "required": true    },    "startDate": {      "kind": "primitive", "type": "date",      "name": "Start Date", "required": true    }  }}/*  An Employee instance carries id, firstName, lastName,  employeeNumber and startDate — three classes, one resolved shape.*/
06

Multiple inheritance

Orthogonal concerns can be composed without forcing them into a single taxonomy. The resolved attribute set is computed deterministically.

{  "ooml": "0.1.0",  "fqn": "com.example.hr/Employee@1.2.0",  "name": "Employee",  "extends": [    "com.example.hr/Person@^1.0.0",    "com.example.common/Auditable@^1.0.0"  ]}/*  Resolution order (depth-first, left to right):    Employee -> Person -> Party -> Auditable   Resolved attribute surface:     Accessor         From        Identity    ------------------------------------------------------------    createdAt        Auditable   common/Auditable@1.0.0#createdAt    updatedAt        Auditable   common/Auditable@1.0.0#updatedAt    id               Party       hr/Party@1.1.0#id    firstName        Person      hr/Person@1.0.0#firstName    lastName         Person      hr/Person@1.0.0#lastName    employeeNumber   Employee    hr/Employee@1.2.0#employeeNumber    startDate        Employee    hr/Employee@1.2.0#startDate     (namespaces abbreviated for width)   Attributes are identified by their fully qualified name, not by their  local name, so two ancestors contributing the same local name are  simply two different attributes rather than a conflict to arbitrate.*/
07

Enumerations as classes

OOML has no separate enumeration artefact. A class acts as the root of a category and its subtypes are the values — which means enum values can carry attributes and be versioned like anything else.

{  "ooml": "0.1.0",  "fqn": "com.example.hr/EmploymentType@1.0.0",  "name": "Employment Type",  "description": "The nature of an employment relationship. Subtypes are the values.",  "abstract": true}{  "ooml": "0.1.0",  "fqn": "com.example.hr/FullTime@1.0.0",  "name": "Full Time",  "extends": ["com.example.hr/EmploymentType@^1.0.0"],  "attributes": {    "weeklyHours": {      "kind": "primitive", "type": "uint8",      "name": "Weekly Hours",      "required": true,      "description": "Something a conventional enum value could not express."    }  }}/* Referenced from a class: */"employmentType": {  "kind": "enum",  "type": "com.example.hr/EmploymentType@^1.0.0",  "name": "Employment Type",  "required": true,  "description": "Valid values are subtypes of the root, excluding the root itself."}
Reuse across models

Definitions that should mean the same thing wherever they appear, and the adjustments a consuming model can make.

08

Reusable global attributes

A global attribute is a shared, independently versioned definition of one piece of information. Classes reference it instead of redefining an equivalent field.

{  "ooml": "0.1.0",  "fqn": "com.example.finance/salary@1.0.0",  "name": "Annual Salary",  "description": "An annual gross salary in the organisation's base currency.",  "kind": "primitive",  "type": "decimal",  "precision": 14,  "scale": 2,  "minimum": 0}/*  Global attribute names are camelCase, distinguishing them from  PascalCase class names: a global attribute is a reusable attribute,  never a type that gets instantiated or extended.   Referenced from a class:*/"annualSalary": {  "kind": "attribute",  "type": "com.example.finance/salary@^1.0.0",  "name": "Annual Salary",  "nullable": true}
09

Renaming and overriding what you inherit

Inherited definitions cannot be silently redeclared. Where a subclass needs a clearer local name or a tighter constraint, it says so explicitly.

{  "ooml": "0.1.0",  "fqn": "com.example.planets/CelestialBody@1.0.0",  "name": "Celestial Body",  "extends": [    "com.example.planets/Surface@^1.0.0",    "com.example.planets/Core@^1.0.0"  ],  "use": {    "com.example.physics/temperature@^1.0.0": {      "as": "surfaceTemperature"    },    "com.example.geology/coreTemperature@^1.0.0": {      "as": "coreTemperature"    },    "gravity": {      "as": "surfaceGravity",      "override": {        "description": "Surface gravity relative to Earth's (1.0 = Earth gravity)."      }    }  }}/*  Both superclasses contribute an attribute locally named 'temperature',  backed by different definitions — so each is picked out by its fully  qualified name. 'gravity' comes from only one place and is already  unambiguous, so the bare name is enough.   Renaming replaces the name rather than adding a second one: at any  point in a hierarchy an attribute has exactly one current name.   Overriding may only narrow, and only a fixed set of properties.  An attribute's kind and type can never be changed by a subclass.*/
Evolution

How models change, and how those changes stay visible to consumers.

10

Independent versioning and dependencies

Each artefact carries its own version, and references it through a range. Those references are the dependencies — there is no separate manifest to keep in step.

objectenumattribute-importmetadataselfAuditable@1.0.0Party@1.1.0Person@1.0.0Department@1.0.0Employee@1.2.0EmploymentType@1.0.0salary@1.0.0SchemaInfo@1.0.0
extends
object
enum
attribute-import
metadata
abstract class
global attribute
metadata schema
/*  MAJOR   A breaking change. Consumers must migrate deliberately.          Removing an attribute, narrowing a type, renaming.   MINOR   A non-breaking addition. Existing valid data stays valid.          A new optional attribute, a widened type, a new enum subtype.   TRIVIAL No structural change at all. Descriptions, names, tags.   A reference states the range it accepts:     "extends": ["com.example.hr/Person@^1.0.0"]   ^1.0.0 accepts any 1.x at or above 1.0.0 — so a superclass can  publish a MINOR change without every descendant being re-released,  while a MAJOR change remains an explicit decision for each consumer.   Because references live in the artefact itself, the dependency graph  is derived rather than declared: 'what depends on this?' is answered  by reading models, not by trusting a manifest.*/
Model metadata

Typed information about the model itself — and the profiles that can be built from it.

11

Attaching model metadata

Metadata keys name the OOML class that defines the metadata's shape, so what is attached here is typed and validatable rather than free-form.

{  "ooml": "0.1.0",  "fqn": "com.example.hr/Employee@1.3.0",  "name": "Employee",  "metadata": {    "com.example.governance/SchemaInfo@^1.0.0": {      "status": "com.example.governance/Published@1.0.0",      "steward": {        "value": "People Systems",        "cascade": true      },      "sourceStandard": {        "value": "HR-REF-114",        "cascade": false      },      "retentionClass": {        "value": null,        "required": true      }    }  }}/*  cascade    the value carries down to subclasses that set none of their own  final      subclasses may not override the value  local      the entry applies to this artefact only and is not inherited   An entry whose value is null but which is required is a declaration  that some class in the hierarchy must supply the value before the  model is considered complete — which is how a governance requirement  becomes a validation result rather than a review comment.*/
12

Governance metadata profile

Define governance once. Require, inherit, override, and lock metadata across a model hierarchy.

CustomerRecord
@1.0.0 · concrete
GovernedDataAsset
@1.0.0 · abstract
GovernanceProfile
@1.0.0 · metadata schema

extends — inheritance: CustomerRecord is a GovernedDataAsset. metadata — dependency: GovernedDataAsset is governed by the profile, and cannot be resolved without it.

Naming a metadata schema's FQN range as a metadata key creates a dependency edge, exactly as extends does for inheritance.

1 · Define the metadata profile

A metadata schema is an ordinary OOML class. GovernanceProfile carries no marker distinguishing it from a domain class — what makes it a profile is that other artefacts name it as a metadata key. Its attributes are typed, so every governance value attached anywhere in the model is validatable.

com.example.governance/GovernanceProfile@1.0.0
{  "ooml": "0.1.0",  "fqn": "com.example.governance/GovernanceProfile@1.0.0",  "name": "Governance Profile",  "description": "Governance annotations for data assets.",  "attributes": {    "owner": {      "kind": "primitive",      "type": "string",      "name": "Owner",      "required": true,      "description": "Domain accountable for this model."    },    "steward": {      "kind": "primitive",      "type": "string",      "name": "Steward",      "required": true,      "description": "Team maintaining the definition."    },    "classification": {      "kind": "enum",      "type": "com.example.governance/DataClassification@^1.0.0",      "name": "Classification",      "required": true,      "description": "Sensitivity classification."    },    "retentionPeriod": {      "kind": "primitive",      "type": "duration",      "name": "Retention Period",      "required": true,      "description": "Retention duration, ISO 8601."    },    "governingPolicy": {      "kind": "primitive",      "type": "uri",      "name": "Governing Policy",      "required": true,      "description": "URI of the governing policy document."    },    "lastReviewDate": {      "kind": "primitive",      "type": "date",      "name": "Last Review Date",      "description": "When the governance entry was last reviewed."    },    "criticalDataElement": {      "kind": "primitive",      "type": "boolean",      "name": "Critical Data Element",      "description": "Whether the asset is a critical data element."    }  }}

1b · The classification enum

classification is an enum attribute, so its permitted values are the subtypes of an enum root — ordinary classes again, here kept in a sub-namespace of their own.

com.example.governance/DataClassification@1.0.0
{  "ooml": "0.1.0",  "fqn": "com.example.governance/DataClassification@1.0.0",  "name": "Data Classification",  "description": "Enum root for sensitivity classifications.",  "abstract": true}
com.example.governance.classification/Internal@1.0.0
{  "ooml": "0.1.0",  "fqn": "com.example.governance.classification/Internal@1.0.0",  "name": "Internal",  "description": "Disclosable within the organisation.",  "abstract": true,  "extends": ["com.example.governance/DataClassification@^1.0.0"]}
com.example.governance.classification/Restricted@1.0.0
{  "ooml": "0.1.0",  "fqn": "com.example.governance.classification/Restricted@1.0.0",  "name": "Restricted",  "description": "Disclosable only to named roles.",  "abstract": true,  "extends": ["com.example.governance/DataClassification@^1.0.0"]}

2 · Declare governance requirements on the base class

GovernedDataAsset applies the profile by naming its FQN range as a metadata key, which creates a metadata dependency edge from the asset to the profile. The controls do the governing: owner and steward are slot declarations — value: null with required: true — obliging some class in the chain to supply them. classification cascades a default that descendants may override. retentionPeriod and governingPolicy cascade and are final, so no descendant may change them. lastReviewDate is local and is not inherited at all.

com.example.governance/GovernedDataAsset@1.0.0
{  "ooml": "0.1.0",  "fqn": "com.example.governance/GovernedDataAsset@1.0.0",  "name": "Governed Data Asset",  "description": "Base class for assets under data governance.",  "abstract": true,  "metadata": {    "com.example.governance/GovernanceProfile@^1.0.0": {      "owner": { "value": null, "required": true },      "steward": { "value": null, "required": true },      "classification": {        "value": "com.example.governance.classification/Internal@1.0.0",        "cascade": true      },      "retentionPeriod": { "value": "P7Y", "cascade": true, "final": true },      "governingPolicy": {        "value": "https://policy.example.org/data-governance/v3",        "cascade": true,        "final": true      },      "lastReviewDate": { "value": "2026-02-01", "local": true }    }  }}

3 · Satisfy and specialize the profile

CustomerRecord fills both slot declarations and cascades them onward, narrows the classification from Internal to Restricted, and adds two entries of its own that stay local to it. It says nothing about retention or policy: those arrive already settled.

com.example.governance/CustomerRecord@1.0.0
{  "ooml": "0.1.0",  "fqn": "com.example.governance/CustomerRecord@1.0.0",  "name": "Customer Record",  "description": "A customer as held by the customer domain.",  "extends": ["com.example.governance/GovernedDataAsset@^1.0.0"],  "metadata": {    "com.example.governance/GovernanceProfile@^1.0.0": {      "owner": { "value": "Customer Domain", "cascade": true },      "steward": { "value": "Customer Data Office", "cascade": true },      "classification": "com.example.governance.classification/Restricted@1.0.0",      "lastReviewDate": { "value": "2026-08-15", "local": true },      "criticalDataElement": { "value": true, "local": true }    }  },  "attributes": {    "customerId": {      "kind": "primitive",      "type": "uuid",      "name": "Customer Identifier",      "required": true    },    "emailAddress": {      "kind": "primitive",      "type": "string",      "name": "Email Address",      "required": true    },    "createdAt": {      "kind": "primitive",      "type": "datetime",      "name": "Created At",      "required": true    }  }}

4 · Inspect the resolved metadata

Reading the chain from GovernedDataAsset down to CustomerRecord gives one effective value per attribute, each traceable to where it was set and why it applies.

AttributeEffective valueHow it got there
ownerCustomer Domainsupplied by CustomerRecord
stewardCustomer Data Officesupplied by CustomerRecord
classificationRestrictedoverrides the cascaded Internal
retentionPeriodP7Yinherited · cascaded · final
governingPolicypolicy.example.org/…/v3inherited · cascaded · final
lastReviewDate2026-08-15local to CustomerRecord
criticalDataElementtruelocal to CustomerRecord

5 · A rejected override

A subclass of CustomerRecord tries to shorten the retention period. Everything else it needs arrives by cascade, so this is the only thing wrong with it.

com.example.governance/ArchivedCustomerRecord@1.0.0 — invalid
{  "ooml": "0.1.0",  "fqn": "com.example.governance/ArchivedCustomerRecord@1.0.0",  "name": "Archived Customer Record",  "description": "Attempts to shorten a final retention period.",  "extends": ["com.example.governance/CustomerRecord@^1.0.0"],  "metadata": {    "com.example.governance/GovernanceProfile@^1.0.0": {      "retentionPeriod": { "value": "P1Y" }    }  }}
Rejected: retentionPeriod is final in GovernedDataAsset and cannot be overridden by a subclass (M02). Note that this document is structurally well-formed and passes the JSON Schema — final enforcement is a cross-artefact rule, checkable only once the inheritance chain is resolved.
Still to be written

Planned examples

The following examples are anticipated by this page’s structure but have not been authored yet. Those describing projections into other technologies depend on generators that do not exist today.

Namespaces and cross-model reuse
Composing a domain from artefacts owned by different teams and namespaces.
An external-standard mapping profile
Recording correspondence between a model and a definition in an external vocabulary.
A small multi-domain model
Several namespaces, shared global attributes and deliberate version boundaries.
OOML → JSON Schema
A projection into JSON Schema. Requires a generator, which does not yet exist.
OOML → UML visualization
Rendering an OOML model as a class diagram. Requires tooling that does not yet exist.
OOML → OWL with ontology metadata
Combining a model with an ontology profile to produce OWL. Requires a generator that does not yet exist.