Specification scope: Core with the Relations companion specification.

Declare relation cardinality. Do not infer it from a name. orders sounds like a collection and customer sounds singular. A processor cannot derive an instance shape from either spelling. The relations extension declares that shape with cardinality, next to the targettype it qualifies.

Identity starts with the target type

Every relation names a targettype, and that target must declare identity. The identity is an ordered array of property names. One name produces a scalar relation identity. Several names produce an array in the declared order.

This order book model declares relations in both directions and makes each shape explicit:

{
  "$schema": "https://json-structure.org/meta/relations/v0/#",
  "$id": "https://schemas.example.com/commerce/order-book/v1",
  "$uses": ["JSONStructureRelations"],
  "name": "OrderBookDocument",
  "$root": "#/definitions/OrderBook",
  "definitions": {
    "Customer": {
      "name": "Customer",
      "type": "object",
      "identity": ["customerNumber"],
      "relations": {
        "orders": {
          "targettype": { "$ref": "#/definitions/Order" },
          "cardinality": "multiple",
          "scope": "#/definitions/OrderBook/properties/orders"
        }
      },
      "properties": {
        "customerNumber": { "type": "string" },
        "name": { "type": "string" }
      },
      "required": ["customerNumber", "name"],
      "additionalProperties": false
    },
    "Order": {
      "name": "Order",
      "type": "object",
      "identity": ["orderNumber"],
      "relations": {
        "customer": {
          "targettype": { "$ref": "#/definitions/Customer" },
          "cardinality": "single",
          "scope": "#/definitions/OrderBook/properties/customers"
        }
      },
      "properties": {
        "orderNumber": { "type": "string" },
        "total": { "type": "decimal" }
      },
      "required": ["orderNumber", "total"],
      "additionalProperties": false
    },
    "OrderBook": {
      "name": "OrderBook",
      "type": "object",
      "properties": {
        "customers": {
          "type": "array",
          "items": { "type": { "$ref": "#/definitions/Customer" } }
        },
        "orders": {
          "type": "array",
          "items": { "type": { "$ref": "#/definitions/Order" } }
        }
      },
      "required": ["customers", "orders"],
      "additionalProperties": false
    }
  }
}

The instance shape follows the declarations:

{
  "$schema": "https://schemas.example.com/commerce/order-book/v1",
  "customers": [
    {
      "customerNumber": "C-1042",
      "name": "Ada Computing GmbH",
      "orders": [
        { "identity": "O-9001" },
        { "identity": "O-9002" }
      ]
    },
    {
      "customerNumber": "C-2048",
      "name": "Babbage Works Ltd",
      "orders": []
    }
  ],
  "orders": [
    {
      "orderNumber": "O-9001",
      "total": "149.50",
      "customer": { "identity": "C-1042" }
    },
    {
      "orderNumber": "O-9002",
      "total": "87.25",
      "customer": { "identity": "C-1042" }
    }
  ]
}

single and multiple use different JSON shapes

"cardinality": "single" means exactly one target and uses one relation object, rather than an array of length one. "cardinality": "multiple" means zero or more targets and uses an array of relation objects. An empty array states that the relation currently has no targets.

The relation object carries identity; it does not embed the target object. That keeps association separate from containment. For a composite target identity such as ["catalogNumber", "revision"], the value becomes an array such as {"identity": ["A-17", 3]}.

Relation names are not ordinary properties

Relations are declared under relations, not under properties, although they appear as properties in an instance. The two declaration maps share a namespace, so a type cannot declare both an ordinary property and a relation named customer.

Without the shared namespace, a customer property containing an object and a customer relation containing an identity would compete for the same JSON member.

Scope identifies the target collection

scope identifies the array, set, or map that contains candidate targets. The resolver searches its elements or map values for a target whose declared identity matches the relation object.

Cardinality does not establish uniqueness. The target’s identity values must be unique within the relevant identity scope, and a relations-aware processor must check that semantic rule. A core validator can verify JSON shape while still missing a duplicate customer or a dangling order reference. The relation declares collection shape, the target declares identity, and the scope tells a resolver where to look. The spelling of orders declares none of them.