ADR 048: JSON Schema 2020-12 as the canonical modelling format
Date: 2026-07-15
Status: Accepted
Decision Makers: @derlinne, @luckey
Context
Model Forge needs a single canonical format in which all Elements are modelled, without inventing a separate metamodel.
Decision
All Elements are represented as JSON Schema 2020-12 documents. There is no separate class/slot/attribute metamodel.
Rationale
- JSON Schema is an open, widely adopted standard with an extensive toolchain (Ajv, networknt, json-schema-to-typescript).
- Inheritance (
allOf), polymorphism (oneOf) and compositional variants (anyOf) are native JSON Schema constructs. $refwith URN values represents the reference graph directly in the schema — no secondary model needed.- The domain truth lives entirely in the schema document itself.
Constraints are native; extensions only annotate
Per-attribute constraints are native JSON Schema keywords, not extensions: pattern,
format, minLength/maxLength, minimum/maximum/exclusiveMinimum/multipleOf,
enum/const, minItems/maxItems/uniqueItems, dependentRequired/dependentSchemas,
if/then/else, unevaluatedProperties. Cross-field rules belong there as well — a
verbose if/then beats a custom keyword, because validation, views and generators then
handle it for free (see
Data Modelling).
The x-* extension vocabulary annotates; it never constrains. An annotation may carry
meaning JSON Schema cannot express, but it must not decide whether instance data is valid — a
constraining x- keyword would produce documents that a standard validator accepts and Model
Forge rejects. This holds for every annotation, present and future; the authoritative roster
is CORE JSON Schema Extensions,
not this ADR.
Annotations legitimately describe three different things, and none of them narrows the set of valid instances:
| What it describes | Example | Why it is safe |
|---|---|---|
| the meaning of an instance value | x-core-ref — the string is a foreign key | the value is still just a string to a validator; the native pattern does the validating |
| the document's provenance | x-xsd-source — which version carries the source XSD | says nothing about instance data at all |
| the model's presentation | x-ui-position — editor canvas position | design-time metadata; never reaches an instance |
x-core-ref (ADR 053) is the reference implementation of the first, hardest kind:
the native keyword validates ("pattern": "^urn:"), the annotation carries the meaning (which
artifact type the URN targets), and Model Forge adds a registry-aware existence check on
import — strictly additional, never a substitute for validation. A new annotation of that kind
follows the same three-part shape.
This is what bounds "no separate metamodel" in practice. It is still JSON Schema as long as:
- a standard validator, given the document alone, can validate instances correctly — the domain truth stays in the document;
- everything that must be enforced is native, or compiled down to native keywords;
- attributes and types are expressed as
propertiesandtype, never in a parallelx-vocabulary such asx-attributes: [{name, type, constraints}].
A constraint JSON Schema genuinely cannot express (a checksum, membership in an external
codelist) is an annotation plus an import-time check, with the accepted asymmetry that it is
enforced only where Model Forge runs. Codelists have a native answer first: enum, or a
$ref to an Enumeration Element.
See also
- JSON Schema Reference
- Data Modelling: the construct-to-JSON-Schema mapping, including validation keywords
- ADR 053: x-core-ref as a typed foreign-key annotation