Object-Oriented Modelling Language
A JSON-based schema language that applies object-oriented concepts — namespacing, multi-inheritance, type safety — and a software-distribution versioning model to the world of data modelling. No query language. No storage engine. Just the model.
A shared language for data, at any scale
Data modelling today is fragmented. Every tool — ERDs, JSON Schema, Avro, Protobuf, OpenAPI, custom wikis — speaks a different dialect. OOML proposes a single, human-readable format expressive enough for engineers yet accessible enough for product owners and analysts.
Business & Tech Agnostic
OOML describes the shape of data — not how it is stored, queried, or serialised. The same model can govern a relational schema, a document store, an API contract, or a data catalogue.
OOP-Inspired Semantics
Classes, multi-inheritance, type hierarchies, attributes, and controlled renaming/overriding give teams the vocabulary to model complex domains without ambiguity — familiar to any software engineer.
Class-Level Versioning
Each class carries its own semantic version. There is no collective package to version. Dependency edges are direct class-to-class relationships — precise, traversable, and meaningful.
Plain JSON
The canonical format is UTF-8 JSON. No proprietary tooling is required to read or write a model. Every artefact is self-describing — its identity is carried within the document itself.
- ›A vocabulary and JSON encoding for classes, attributes, and their relationships
- ›A versioning contract with clear semantics for breaking and non-breaking changes
- ›A namespace and identity system enabling global, unambiguous class references
- ›An inheritance mechanism for attribute reuse and type hierarchy
- ›A class-to-class dependency model forming a directed acyclic graph (DAG)
- ›A metadata model for structured, typed, versioned annotations
- ›A query language
- ›A storage engine or persistence format
- ›A serialisation format for data instances
- ›A protocol for data exchange
- ›A versioned package or bundle concept
- ›A registry service, its wire protocol, or its governance model
"The central insight of OOML is that the class, not a collection of classes, is the correct unit of versioning for data models. This keeps the dependency graph simple and its edges meaningful: every edge represents an actual structural relationship between two classes."
— OOML Specification, §1
The building blocks of OOML
Thirteen concepts form the complete vocabulary of OOML. Understanding them is sufficient to read, write, and reason about any valid model.
classClass
The central construct of OOML. A versioned, named, uniquely identifiable collection of attributes. Each class is an independent artefact with its own FQN and semantic version.
Classes may be abstract (cannot be instantiated, only extended) or final (cannot be extended). A class is self-describing: its identity is carried within its own JSON document. The class — not any grouping layer — is the unit of versioning.
fqnFully Qualified Name
The globally unique identity of a class or global attribute. Format: namespace/ClassName@version, e.g. com.example.hr/Employee@1.2.0.
The namespace follows reverse-domain convention (all lowercase). The class name is PascalCase. The version is an exact semver. Owned attributes extend the class FQN with a # fragment: com.example.hr/Employee@1.2.0#employeeNumber. A sub-attribute nested inside a nested attribute uses a dotted path, e.g. #homeAddress.streetName — a positional address, not an independent identity.
namespaceNamespace
A reverse-domain organisational scope that groups related classes and governs who may publish names within it, e.g. com.example.hr or io.mycompany.platform.
All namespace segments are lowercase. A class name must be unique within its namespace. There is no package or module layer between the namespace and individual class artefacts.
attributeAttribute
A named, typed position within a class. Every attribute has a kind that classifies its structural role: primitive, object, class, enum, list, set, map, attribute (reference to a global attribute), or nested.
Attributes may be required, nullable, static, local, or final. The attribute identifier (camelCase JSON key) is distinct from the human-readable name property. Inherited attributes cannot be overridden — only extended by adding new attributes in a subclass. object references an instance by identity; class references a class itself (a type-tag, not a record); nested embeds an ad hoc structure inline with no identity of its own.
globalattrGlobal Attribute
A first-class, independently versioned reusable semantic contract for a typed attribute — independent of any class. Use for domain concepts that are genuinely global, such as monetaryAmount or geoCoordinate.
A global attribute has its own FQN, version, kind, and type constraints, and is named in camelCase — deliberately distinct from a class's PascalCase, since it is never instantiated or extended. A class references one using "kind": "attribute". The attribute identifier within the class is the local accessor; the global attribute FQN is the semantic identity. Both are independent.
inheritanceInheritance
A class declares one or more superclasses via extends. It inherits all non-local attributes from all ancestors. OOML supports multiple inheritance.
Attribute resolution follows a depth-first, left-to-right MRO (C3 linearisation). Inherited attributes cannot be re-declared or overridden. The diamond problem does not arise because attributes are identified by FQN, not by local name — two ancestors sharing a local name hold distinct attributes.
enumrootEnum Root
An ordinary class whose subtypes (excluding itself) define the valid values of an enum attribute. Enum members are real classes — they can carry attributes and be independently versioned.
An attribute with "kind": "enum" accepts a reference to any class that is a subtype of the named root, excluding the root itself. Adding a new subclass to the root is a non-breaking MINOR change; removing one is a MAJOR breaking change.
useRenaming & Overriding (use)
A class adjusts an inherited attribute — without redeclaring it, which is disallowed — via the use property. Each entry may rename the attribute (as) and/or narrow a limited set of its properties (override).
use replaces aliasing entirely: renaming is non-additive — the old name becomes unreachable, not aliased alongside the new one. Keys are either a bare attribute identifier (when unambiguous) or an FQN range (to pick one attribute out from under a name collision). Only a fixed list of properties are override-eligible, and narrowing rules apply.
metadataMetadata
Structured, typed, versioned annotations on any OOML artefact. Metadata schemas are ordinary OOML classes — the language uses itself as the metadata modelling language.
The metadata object on a class maps a metadata schema FQN range to a set of typed attribute values. Metadata entries support cascade (propagate to subclasses), final (cannot be overridden), and local (scoped to the declaring artefact only).
depgraphDependency Graph
A directed acyclic graph (DAG) of class-to-class and class-to-global-attribute edges. There is no separate dependencies property — every edge is derived directly from a class's own body.
Edge types include: extends (inheritance), object (instance reference), class (class-itself reference), enum (enum root), attribute-import, and metadata. use introduces no edge of its own — it only adjusts an attribute already reachable via an existing extends or attribute-import edge. Self-referential object, class, and enum edges are exempt from cycle detection — only extends, attribute-import, and metadata create structural cycles. "What depends on this class?" is a direct graph-edge lookup — no scan, no declared manifest.
nestedNested Attribute
An ad hoc, locally-scoped structure with no independent identity, described inline by its own attributes property. Use "kind": "nested" instead of defining a separate class for a shape that only ever appears in one place.
A nested attribute has no type — its shape is entirely defined by sub-attributes, which may themselves be nested to any depth. Sub-attributes have no FQN of their own; dependencies they introduce attach to the nearest containing class or global attribute. A collection (list, set, map) may hold nested values via valueKind: "nested", with valueType carrying the inline sub-attribute map.
versioningVersioning
Every class and global attribute carries a semantic version (MAJOR.MINOR.TRIVIAL). Artefact versions are treated as immutable once distributed.
MAJOR: breaking changes (removing/renaming attributes, narrowing types). MINOR: non-breaking additions (new optional attributes, widening types, new enum subtypes). TRIVIAL: documentation and metadata edits only. Version ranges follow npm-compatible semver syntax (^, ~, >=).
selfThe self Reference
An authoring token used in a type position to mean "the class in which this attribute is declared". Expanded to the class's own FQN range before distribution.
self solves the chicken-and-egg problem of a class needing to reference itself before its version is known. It resolves to the declaring class, not the inheriting class — keeping the dependency graph acyclic and attribute types unambiguous across the hierarchy.
OOML in plain JSON
Seven annotated examples drawn directly from the specification — from standalone global attributes to derived dependency graph queries.
Global attributes are first-class, independently versioned artefacts. They define a reusable semantic contract for a typed attribute — independent of any class. Note the camelCase name: global attributes are never instantiated or extended, so they take camelCase rather than a class's PascalCase. Here, salary is published as a standalone artefact usable by any class that imports it.
{
"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
}Terminology reference
37 of 37 terms shown