Annex D: Internal Schema References (Normative)

Version: 1.6.0 Date: April 2026 Status: Draft

License: This annex is part of the Open Okyline Language Specification and is subject to the same license terms (CC BY-SA 4.0). See the Core Specification for full license details.

This annex defines the internal reference mechanism for composing and reusing schema fragments within a single document. It introduces $defs for declaring reusable templates, $ref for including them, and $override/$remove for adapting included structures. This enables DRY (Don’t Repeat Yourself) schema design while maintaining full control over structural composition.


Relation to the Core Specification

This annex defines the semantics of:

It specifies how Okyline schemas can be composed and extended using internal definitions, promoting reuse and consistency within a single document.

For versioned imports and external schema references, see Annex E — External Imports and Versioning.


D.1 Overview

Okyline supports schema references to promote reuse, modularity, and consistency within a schema document.

References allow a field or an object definition to reuse an existing schema fragment identified by a logical name stored in $defs.

Okyline distinguishes two complementary use cases:

  1. Property-level reference — a single field whose type is taken from a definition (field | $ref).
  2. Object-level reference — an object that includes all fields of another definition and may add, override or remove fields. Object-level reference targets exactly one template.

In Okyline, $ref is an inclusion mechanism: a referenced schema is treated as a base that can be extended, explicitly overridden or partially removed. This differs intentionally from JSON Schema, where $ref behaves as a total substitution.


D.2 Definition Repository — $defs

Okyline reserves the special block $defs as a container for reusable schema fragments. It is declared at the root level, alongside $oky.

D.2.1 Syntax

{
  "$oky": {
    "person": {
      "$ref": "&Address",
      "name|@ {2,50}": "Dupond"
    }
  },
  "$defs": {
    "Address": {
      "street|@ {2,100}": "12 rue du Saule",
      "city|@ {2,100}": "Lyon"
    }
  }
}

D.2.2 Normative Rules

D.2.3 Scalar Definitions

$defs supports both object schemas and scalar type definitions:

{
  "$oky": {
    "user": {
      "email | $ref @": "&Email",
      "score | $ref": "&Percentage"
    }
  },
  "$defs": {
    "Email|~$Email~ {5,100}": "user@example.com",
    "Percentage|(0..100)": 50,
    "Address": {
      "street|@ {2,100}": "12 rue du Saule",
      "city|@ {2,100}": "Lyon"
    }
  }
}

Scalar definitions follow standard Okyline syntax: the key contains the name and constraints, the value is the example.


D.3 Reference Syntax

Within a document, definitions are referenced via:

&Name

Where Name is defined in $defs.

Examples:

&Address
&Email
&Person

Rules:

Note: For external references to definitions in other schemas, see Annex E.


D.4 Property-Level References — field | $ref

D.4.1 Syntax

A field can use a definition as its type via the $ref constraint suffix:

{
  "$oky": {
    "person": {
      "address | $ref": "&Address",
      "name|@ {2,50}": "Dupond"
    }
  },
  "$defs": {
    "Address": {
      "street|@ {2,100}": "12 rue du Saule",
      "city|@ {2,100}": "Lyon"
    }
  }
}

D.4.2 Semantics

A property-level $ref MAY target:

D.4.3 Lists of Referenced Elements

If the value associated with field | $ref is a single-element array containing a reference string, the field is interpreted as a list whose elements use the referenced schema:

{
  "$oky": {
    "company": {
      "addresses | $ref": ["&Address"]
    }
  },
  "$defs": {
    "Address": {
      "street|@ {2,100}": "12 rue du Saule",
      "city|@ {2,100}": "Lyon"
    }
  }
}

Semantics:

List size constraints can be combined:

"addresses | $ref [1,10]": ["&Address"]

D.4.4 Constraint Categories

Okyline distinguishes two categories of constraints with different behaviors:

Structural Constraints (contextual, defined at usage)

These constraints depend on the context of use and are NOT included from the referenced definition:

Constraint Description
@ Required
? Nullable
[min,max] List size
! Uniqueness in list
% Default value
Label Field description

The same Address can be required in Order but optional in UserProfile.

Value Constraints (intrinsic, included)

These constraints define the contract of the type itself and ARE included from the referenced definition. They can only be modified via $override:

Constraint Description
# Key field(s) for object identity
{min,max} String length
(min..max) Numeric range
('A','B') Enumeration
~pattern~ Regex/format
(%Compute) Computed validation

Example:

{
  "$oky": {
    "user": {
      "primaryEmail | $ref @": "&Email",
      "backupEmail | $ref ?": "&Email"
    }
  },
  "$defs": {
    "Email|~$Email~ {5,100}": "user@example.com"
  }
}

D.4.5 Example Values

Example values are included from the referenced definition. They can be modified via $override if needed.


D.5 Object-Level References — Structural Composition

D.5.1 Basic Inclusion

An object schema can include another definition as a template using a top-level $ref field:

{
  "$oky": {
    "Person": {
      "$ref": "&Address",
      "name|@ {2,50}": "Dupond"
    }
  },
  "$defs": {
    "Address": {
      "street|@ {2,100}": "12 rue du Saule",
      "city|@ {2,100}": "Lyon"
    }
  }
}

Semantics:

The effective Person schema is conceptually equivalent to:

{
  "street|@ {2,100}": "12 rue du Saule",
  "city|@ {2,100}": "Lyon",
  "name|@ {2,50}": "Dupond"
}

D.5.2 Target Type

For object-level inclusion:

For property-level $ref (section D.4), the target MAY be scalar, object or array.

D.5.3 Structural Composition Rules

Object-level $ref is a structural composition: it merges the fields of a template into the current object. Each object-level $ref targets exactly one template.

When a template contains conditional rules ($requiredIf, $forbiddenIf, $appliedIf, etc.) or $compute expressions, these stateful elements are injected into the including object alongside the template’s fields. To preserve the semantic integrity of these included elements, the following restriction applies:

This restriction prevents broken references (removing a field that an included conditional rule depends on, which would produce an unsatisfiable schema).

Templates containing only fields (no conditional rules, no $compute) are unaffected by this restriction.

D.5.4 Field Collision Rules

When a schema includes a template via $ref:

D.5.5 Single Inclusion

The value of $ref MUST be a single reference string (e.g. "&Address"). An object-level $ref targets exactly one template.

D.5.6 Cycles


D.6 Template Adaptation — $remove

Okyline provides the $remove directive to exclude fields from included templates.

D.6.1 Syntax

{
  "$oky": {
    "AnonymousPerson": {
      "$ref": "&Person",
      "$remove": ["email", "ssn"]
    }
  },
  "$defs": {
    "Person": {
      "name|@ {1,50}": "John",
      "age|@ (0..150)": 42,
      "email|@ ~$Email~": "john@example.com",
      "ssn|@": "123-45-6789"
    }
  }
}

D.6.2 Semantics

Effective AnonymousPerson schema:

{
  "name|@ {1,50}": "John",
  "age|@ (0..150)": 42
}

D.6.3 Rules


D.7 Field Adaptation — $override and $amend

Okyline provides two directives to adapt a field inherited from a template: $override replaces the field entirely, $amend replaces only the constraint blocks specified by the adapter. Both produce a new effective field by merging the base field (from the referenced template) with the adapter.

D.7.1 Syntax

Both directives appear as a suffix after the pipe in a field declaration, at the same position as standard field constraints:

"fieldName | $override <constraints>": <exampleValue>
"fieldName | $amend <constraints>": <exampleValue>

$override and $amend MUST NOT appear on the same field declaration.

D.7.2 Invariants (both directives)

The following aspects of the base field MUST be preserved by both $override and $amend:

Any attempt to change one of these via $override or $amend MUST cause a schema parsing error.

D.7.3 Merge Semantics

Let the base be the inherited field and the adapter be the $override or $amend declaration. The effective field is computed block-by-block:

After the merge, the effective field carries exactly one block of each type, consistent with the single-constraint-per-type invariant defined in the Core Specification.

D.7.4 Rules

D.7.5 Example

{
  "$oky": {
    "Employee": {
      "$ref": "&Person",
      "name | $amend @": "John Doe",
      "salary|@ (>=0)": 3000
    }
  },
  "$defs": {
    "Person": {
      "name|? {1,50}": "John",
      "age|@ (0..150)": 42
    }
  }
}

Effective Employee schema (after merge of name via $amend @):

{
  "name|@? {1,50}": "John Doe",
  "age|@ (0..150)": 42,
  "salary|@ (>=0)": 3000
}

The @ flag is added by $amend; the ? flag, the string length {1,50} and the example are kept from the base. Using $override @ instead would yield name|@, erasing {1,50}, ? and the base example.


D.8 Order of Application

For an object that uses $ref, $remove, $override, $amend and local fields, the effective schema is computed conceptually in the following order:

  1. Template injection — Resolve the single $ref to its template and inject all fields.
  2. Removals — Apply all $remove directives.
  3. Adaptations — Apply all $override and $amend directives via the field merge defined in D.7.3.
  4. Local additions — Add remaining locally-declared fields.

D.8.1 Error Conditions

Situation Error
$ref value is not a single reference string Schema rejected
$remove targets non-existent field Schema rejected
$override or $amend targets non-existent field (after removes) Schema rejected
$override and $amend both specified on the same field declaration Schema rejected
$override or $amend attempts to change field type, collection nature or $ref target Schema rejected
Local field collides with included (no $override or $amend) Schema rejected
Object-level cycle detected Schema rejected
$remove used with a $ref targeting a template containing conditional rules or $compute Schema rejected

D.9 Interaction with Conditional Directives

$ref, $override and $remove apply only to structural fields of an object.

A structural field is a field that is part of the object’s schema after:

  1. applying object-level $ref inclusion,
  2. applying all $remove directives,
  3. applying all $override directives,
  4. adding local fields declared at the same level.

Fields that exist only inside $appliedIf (or related conditional) branches are not considered structural fields of the base object.

D.9.1 Rules

D.9.2 Validation at Load Time

During schema loading, conditional directives are interpreted after structural resolution is complete:

If a conditional directive references a field that does not exist according to these rules, the schema MUST be rejected.


D.10 Summary

Feature Description
$defs Repository for reusable templates (root level, first level only)
&Name Reference syntax (resolves in $defs)
field \| $ref Reuse a template as the type of a field (scalar, object, or array)
field \| $ref: ["&Name"] Array whose elements use the referenced template
Object-level $ref Include all fields from a single template — structural composition (single inclusion only)
$override Adapt an included field — replace its definition block-by-block, unspecified blocks are removed (see D.7)
$amend Adapt an included field — replace only specified blocks, unspecified blocks are inherited from the base (see D.7)
$remove Adapt by excluding an included field
Structural constraints @, ?, [...], !, %, labels — local, at usage
Value constraints #, {...}, (...), ~...~, etc. — included, adapt via $override or $amend
Invariants preserved by $override/$amend field type, collection nature, $ref target
Conditional directives / $compute Included when $ref targets a template containing them; $remove is not permitted in that case (see D.5.3); not modifiable via $override/$remove
Object-level cycles Forbidden (detected at load time)
Property-level recursion Allowed

Note: For external references and versioned imports, see Annex E — External Imports and Versioning.


D.11 Complete Example

{
  "$okylineVersion": "1.4.0",
  "$version": "1.0.0",
  "$title": "Order Schema with Internal References",

  "$oky": {
    "order": {
      "$ref": "&Auditable",
      "orderId|@ # ~$OrderId~": "ORD-12345678",
      "customerEmail | $ref @": "&Email",
      "status|@ ($ORDER_STATUS)": "PENDING",
      "items | $ref @ [1,100]": ["&OrderItem"],
      "shippingAddress | $ref @": "&Address",
      "billingAddress | $ref": "&Address",
      "total|@ (%OrderTotal)": 100.50,

      "$requiredIf status('SHIPPED','DELIVERED')": ["trackingNumber"],
      "trackingNumber|{10,50}": "TRACK123456"
    }
  },

  "$defs": {
    "Email|~$Email~ {5,100}": "user@example.com",

    "Auditable": {
      "createdAt|@ ~$DateTime~": "2025-01-01T00:00:00Z",
      "updatedAt|@ ~$DateTime~": "2025-01-01T00:00:00Z"
    },

    "Address": {
      "street|@ {5,100}": "123 Main Street",
      "city|@ {2,50}": "Paris",
      "postalCode|@ {5,10}": "75001",
      "country|@ {2}": "FR"
    },

    "OrderItem": {
      "sku|@ # {5,20}": "SKU-12345",
      "name|@ {2,200}": "Product Name",
      "quantity|@ (1..1000)": 1,
      "unitPrice|@ (>0)": 100.50
    }
  },

  "$format": {
    "OrderId": "^ORD-[0-9]{8}$"
  },

  "$compute": {
    "ItemTotal": "unitPrice * quantity",
    "OrderTotal": "total == sum(items, %ItemTotal)"
  },

  "$nomenclature": {
    "ORDER_STATUS": "PENDING,CONFIRMED,SHIPPED,DELIVERED,CANCELLED",
    "PAYMENT_METHOD": "CARD,PAYPAL,BANK_TRANSFER"
  }
}

Changelog

End of Annex D — Internal Schema References (Normative)