UJG / Editor's Draft /
GitHub
Table of Contents
  • 1Overview
  • 2Extension Attachment
  • 3Processing Model
  • 4Relationship to Domain Requirements
  • 5Domain Model Vocabulary
    • 5.1DomainModel
    • 5.2Common Domain Element
    • 5.3Entity and ValueObject
    • 5.4Property
    • 5.5Association
    • 5.6DomainOperation
    • 5.7Invariant
  • 6Validation Semantics
    • 6.1Internal References
    • 6.2Domain Requirement Resolution
    • 6.3Requirement Coverage
  • 7Realization Boundary
  • 8Normative Artifacts
  • 9Examples
    • 9.1Workshop Waitlist Domain Model
W3C Community Group Draft Report

Domain Model

draft

Status of this Document

This report was published by the User Journal Graph Community Group . It is not a W3C Standard nor is it on the W3C Standards Track. Please note that under the W3C Community Contributor License Agreement (CLA) there is a limited opt-out and other conditions apply. Learn more about W3C Community and Business Groups .

Last Update
2026-08-24
Editors
  • Seva Dolgopolov
Group
User Journal Graph Community Group
Repository
View Source
License
W3C-Software-and-Document

1. Overview

The Domain Model Document Extension defines an optional, technology-neutral application domain model attached to a UJGDocument.

The extension is a Document Extension, not an RDF optional module. Its payload is opaque JSON from the perspective of generic UJG JSON-LD processing. It is validated by JSON Schema and derives traceability from [UJG Domain Requirements].

The Domain Model Extension does not prescribe technical realization.

2. Extension Attachment

The extension value is attached to UJGDocument.extensions using the key org.openuji.domain-model.

json
{
  "@type": "UJGDocument",
  "@id": "https://example.org/journey.jsonld",
  "extensions": {
    "org.openuji.domain-model": {
      "formatVersion": "0.1",
      "id": "urn:domain:model:example",
      "domainRequirementRefs": [],
      "entities": [],
      "valueObjects": [],
      "associations": [],
      "domainOperations": [],
      "invariants": []
    }
  },
  "nodes": []
}
{
  "@type": "UJGDocument",
  "@id": "https://example.org/journey.jsonld",
  "extensions": {
    "org.openuji.domain-model": {
      "formatVersion": "0.1",
      "id": "urn:domain:model:example",
      "domainRequirementRefs": [],
      "entities": [],
      "valueObjects": [],
      "associations": [],
      "domainOperations": [],
      "invariants": []
    }
  },
  "nodes": []
}

The value of extensions["org.openuji.domain-model"] MUST be a DomainModel payload conforming to this specification.

Core MUST NOT define Domain Model-specific properties such as domainModel or domainModelRef.

3. Processing Model

Generic UJG processors treat the Domain Model payload as opaque JSON.

A processor that does not implement this extension MAY ignore its semantics while preserving its JSON payload during non-lossy read-transform-write.

A processor implementing this extension MUST validate the payload using the Domain Model JSON Schema.

A Domain Model-aware validator MUST resolve referenced Domain Requirements, resolve internal Domain Model references, and apply the semantic rules defined by this specification.

Unknown Domain Model semantics MUST NOT affect Core identity, import resolution, reference resolution, Graph traversal, or Graph semantics.

4. Relationship to Domain Requirements

Domain Model traceability flows through Domain Requirements.

flowchart TD
  element[DomainModel element] -->|domainRequirementRefs| requirement[DomainRequirement]
  requirement -->|source-specific requirement reference| semantics[UJG semantics]

The Domain Model Extension MUST NOT duplicate direct references to Conditions, Effects, States, EntryBindings, or other UJG semantic sources for requirement traceability.

Domain Requirements are the boundary between UJG journey semantics and the Domain Model.

5. Domain Model Vocabulary

Version 0.1 defines exactly these payload object types:

  • DomainModel

  • Entity

  • ValueObject

  • Property

  • Association

  • DomainOperation

  • Invariant

classDiagram
  class DomainModel {
    formatVersion
    id
    domainRequirementRefs
    entities
    valueObjects
    associations
    domainOperations
    invariants
  }
  class Entity {
    id
    label
    description
    domainRequirementRefs
    properties
  }
  class ValueObject {
    id
    label
    description
    domainRequirementRefs
    properties
  }
  class Property {
    id
    label
    description
    domainRequirementRefs
    valueType
    allowedValues
  }
  class Association {
    sourceRef
    targetRef
  }
  class DomainOperation {
    actsOnRefs
    preconditions
    postconditions
  }
  class Invariant {
    appliesToRefs
    assertion
  }
  DomainModel --> "0..*" Entity : entities
  DomainModel --> "0..*" ValueObject : valueObjects
  DomainModel --> "0..*" Association : associations
  DomainModel --> "0..*" DomainOperation : domainOperations
  DomainModel --> "0..*" Invariant : invariants
  Entity --> "0..*" Property : properties
  ValueObject --> "0..*" Property : properties

Version 0.1 does not define aggregate boundaries, domain services, domain events, repositories, commands, bounded contexts, persistence models, API models, executable rule languages, or technical architecture.

5.1. DomainModel

A DomainModel is the root payload stored in extensions["org.openuji.domain-model"].

PropertyRequirement
formatVersionMUST be "0.1".
idMUST be a stable identifier for the Domain Model.
domainRequirementRefsMUST declare the complete set of UJG Domain Requirements this Domain Model claims to satisfy.
entitiesMUST exist; MAY be empty.
valueObjectsMUST exist; MAY be empty.
associationsMUST exist; MAY be empty.
domainOperationsMUST exist; MAY be empty.
invariantsMUST exist; MAY be empty.

Every requirement listed in DomainModel.domainRequirementRefs MUST be referenced by at least one contained Domain Model element.

Every domainRequirementRefs value on a contained element MUST also occur in the root DomainModel.domainRequirementRefs.

5.2. Common Domain Element

Every Entity, ValueObject, Property, Association, DomainOperation, and Invariant shares the same basic identification and traceability properties.

PropertyRequirement
idMUST uniquely identify the element within the Domain Model and remain stable while the element's semantics remain stable.
labelMUST provide a concise human-readable domain name.
descriptionMAY provide explanatory human-readable text.
domainRequirementRefsMUST contain at least one reference, contain no duplicates, and contain only references declared by the containing DomainModel.

5.3. Entity and ValueObject

An Entity represents a domain object for which independent identity matters. A ValueObject represents a domain value whose meaning is determined by its value rather than an independent identity lifecycle.

Both object types use the Common Domain Element properties and add:

PropertyRequirement
propertiesMUST be an array of Property.

The specification does not prescribe how entity instances are identified or persisted. A producer SHOULD NOT introduce a ValueObject merely to wrap a primitive value without domain significance.

5.4. Property

A Property describes domain information belonging to an Entity or ValueObject.

In addition to the Common Domain Element properties, a Property has:

PropertyRequirement
valueTypeMUST be one of string, integer, number, boolean, date, or datetime.
allowedValuesMAY restrict the property to a finite set of values.

The specification does not define storage types, programming-language types, columns, nullability, or serialization formats.

5.5. Association

An Association represents a required relationship between domain objects.

In addition to the Common Domain Element properties, an Association has:

PropertyRequirement
sourceRefMUST resolve to an Entity or ValueObject in the same Domain Model.
targetRefMUST resolve to an Entity or ValueObject in the same Domain Model.

Version 0.1 does not standardize cardinality and does not define ORM or persistence relationships.

5.6. DomainOperation

A DomainOperation represents behavior required from the domain.

In addition to the Common Domain Element properties, a DomainOperation has:

PropertyRequirement
actsOnRefsMUST be an array of references to Entity or ValueObject elements in the same Domain Model.
preconditionsMUST be an array of technology-neutral semantic statements.
postconditionsMUST be an array of technology-neutral semantic statements.

Version 0.1 deliberately defines no predicate or expression language. A DomainOperation does not imply an API operation, HTTP endpoint, service method, UI action handler, or execution location.

5.7. Invariant

An Invariant represents a domain rule that must remain true across valid domain behavior.

In addition to the Common Domain Element properties, an Invariant has:

PropertyRequirement
appliesToRefsMUST reference Domain Model elements.
assertionMUST be a technology-neutral semantic statement.

Version 0.1 defines no executable invariant language.

6. Validation Semantics

JSON Schema is responsible only for structural validation. Cross-reference resolution MUST be performed by a Domain Model-aware UJG validator.

6.1. Internal References

All Domain Model element IDs MUST be unique.

Association.sourceRef, Association.targetRef, DomainOperation.actsOnRefs, and Invariant.appliesToRefs MUST resolve within the same Domain Model.

A structurally valid JSON document containing dangling internal references is semantically invalid.

6.2. Domain Requirement Resolution

Every value in domainRequirementRefs MUST resolve to a UJG Domain Requirement available through the containing UJG document and its normal import resolution.

A dangling Domain Requirement reference makes the Domain Model invalid.

6.3. Requirement Coverage

For every Domain Requirement declared by DomainModel.domainRequirementRefs, at least one contained Domain Model element MUST reference that requirement.

This permits one requirement to justify multiple Domain Model elements, and one Domain Model element to satisfy multiple requirements. No one-to-one mapping is implied.

7. Realization Boundary

The Domain Model describes domain semantics.

It MUST NOT prescribe frontend versus backend placement, browser versus server execution, local or remote persistence, SQL or NoSQL, APIs or transport, framework architecture, repositories or controllers, authentication mechanisms, messaging infrastructure, or deployment topology.

8. Normative Artifacts

The normative Domain Model JSON Schema is defined below and is published at https://ujg.specs.openuji.org/ed/extensions/domain-model/domain-model.schema.json.

For namespace-style artifact discovery, the same schema is also published at https://ujg.specs.openuji.org/ed/ns/domain-model.schema.json.

json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://ujg.specs.openuji.org/ed/extensions/domain-model/domain-model.schema.json",
  "title": "UJG Domain Model Extension",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "formatVersion",
    "id",
    "domainRequirementRefs",
    "entities",
    "valueObjects",
    "associations",
    "domainOperations",
    "invariants"
  ],
  "properties": {
    "formatVersion": {
      "const": "0.1"
    },
    "id": {
      "$ref": "#/$defs/identifier"
    },
    "domainRequirementRefs": {
      "$ref": "#/$defs/referenceArray"
    },
    "entities": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/entity"
      }
    },
    "valueObjects": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/valueObject"
      }
    },
    "associations": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/association"
      }
    },
    "domainOperations": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/domainOperation"
      }
    },
    "invariants": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/invariant"
      }
    }
  },
  "$defs": {
    "identifier": {
      "type": "string",
      "minLength": 1
    },
    "referenceArray": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/identifier"
      },
      "uniqueItems": true
    },
    "elementRequirementRefs": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/identifier"
      },
      "minItems": 1,
      "uniqueItems": true
    },
    "property": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "id",
        "label",
        "domainRequirementRefs",
        "valueType"
      ],
      "properties": {
        "id": {
          "$ref": "#/$defs/identifier"
        },
        "label": {
          "type": "string",
          "minLength": 1
        },
        "description": {
          "type": "string"
        },
        "domainRequirementRefs": {
          "$ref": "#/$defs/elementRequirementRefs"
        },
        "valueType": {
          "enum": [
            "string",
            "integer",
            "number",
            "boolean",
            "date",
            "datetime"
          ]
        },
        "allowedValues": {
          "type": "array",
          "items": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "number"
              },
              {
                "type": "integer"
              },
              {
                "type": "boolean"
              }
            ]
          },
          "uniqueItems": true
        }
      }
    },
    "entity": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "id",
        "label",
        "domainRequirementRefs",
        "properties"
      ],
      "properties": {
        "id": {
          "$ref": "#/$defs/identifier"
        },
        "label": {
          "type": "string",
          "minLength": 1
        },
        "description": {
          "type": "string"
        },
        "domainRequirementRefs": {
          "$ref": "#/$defs/elementRequirementRefs"
        },
        "properties": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/property"
          }
        }
      }
    },
    "valueObject": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "id",
        "label",
        "domainRequirementRefs",
        "properties"
      ],
      "properties": {
        "id": {
          "$ref": "#/$defs/identifier"
        },
        "label": {
          "type": "string",
          "minLength": 1
        },
        "description": {
          "type": "string"
        },
        "domainRequirementRefs": {
          "$ref": "#/$defs/elementRequirementRefs"
        },
        "properties": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/property"
          }
        }
      }
    },
    "association": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "id",
        "label",
        "domainRequirementRefs",
        "sourceRef",
        "targetRef"
      ],
      "properties": {
        "id": {
          "$ref": "#/$defs/identifier"
        },
        "label": {
          "type": "string",
          "minLength": 1
        },
        "description": {
          "type": "string"
        },
        "domainRequirementRefs": {
          "$ref": "#/$defs/elementRequirementRefs"
        },
        "sourceRef": {
          "$ref": "#/$defs/identifier"
        },
        "targetRef": {
          "$ref": "#/$defs/identifier"
        }
      }
    },
    "domainOperation": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "id",
        "label",
        "domainRequirementRefs",
        "actsOnRefs",
        "preconditions",
        "postconditions"
      ],
      "properties": {
        "id": {
          "$ref": "#/$defs/identifier"
        },
        "label": {
          "type": "string",
          "minLength": 1
        },
        "description": {
          "type": "string"
        },
        "domainRequirementRefs": {
          "$ref": "#/$defs/elementRequirementRefs"
        },
        "actsOnRefs": {
          "$ref": "#/$defs/referenceArray"
        },
        "preconditions": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "postconditions": {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      }
    },
    "invariant": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "id",
        "label",
        "domainRequirementRefs",
        "appliesToRefs",
        "assertion"
      ],
      "properties": {
        "id": {
          "$ref": "#/$defs/identifier"
        },
        "label": {
          "type": "string",
          "minLength": 1
        },
        "description": {
          "type": "string"
        },
        "domainRequirementRefs": {
          "$ref": "#/$defs/elementRequirementRefs"
        },
        "appliesToRefs": {
          "$ref": "#/$defs/referenceArray"
        },
        "assertion": {
          "type": "string",
          "minLength": 1
        }
      }
    }
  }
}
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://ujg.specs.openuji.org/ed/extensions/domain-model/domain-model.schema.json",
  "title": "UJG Domain Model Extension",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "formatVersion",
    "id",
    "domainRequirementRefs",
    "entities",
    "valueObjects",
    "associations",
    "domainOperations",
    "invariants"
  ],
  "properties": {
    "formatVersion": {
      "const": "0.1"
    },
    "id": {
      "$ref": "#/$defs/identifier"
    },
    "domainRequirementRefs": {
      "$ref": "#/$defs/referenceArray"
    },
    "entities": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/entity"
      }
    },
    "valueObjects": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/valueObject"
      }
    },
    "associations": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/association"
      }
    },
    "domainOperations": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/domainOperation"
      }
    },
    "invariants": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/invariant"
      }
    }
  },
  "$defs": {
    "identifier": {
      "type": "string",
      "minLength": 1
    },
    "referenceArray": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/identifier"
      },
      "uniqueItems": true
    },
    "elementRequirementRefs": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/identifier"
      },
      "minItems": 1,
      "uniqueItems": true
    },
    "property": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "id",
        "label",
        "domainRequirementRefs",
        "valueType"
      ],
      "properties": {
        "id": {
          "$ref": "#/$defs/identifier"
        },
        "label": {
          "type": "string",
          "minLength": 1
        },
        "description": {
          "type": "string"
        },
        "domainRequirementRefs": {
          "$ref": "#/$defs/elementRequirementRefs"
        },
        "valueType": {
          "enum": [
            "string",
            "integer",
            "number",
            "boolean",
            "date",
            "datetime"
          ]
        },
        "allowedValues": {
          "type": "array",
          "items": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "number"
              },
              {
                "type": "integer"
              },
              {
                "type": "boolean"
              }
            ]
          },
          "uniqueItems": true
        }
      }
    },
    "entity": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "id",
        "label",
        "domainRequirementRefs",
        "properties"
      ],
      "properties": {
        "id": {
          "$ref": "#/$defs/identifier"
        },
        "label": {
          "type": "string",
          "minLength": 1
        },
        "description": {
          "type": "string"
        },
        "domainRequirementRefs": {
          "$ref": "#/$defs/elementRequirementRefs"
        },
        "properties": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/property"
          }
        }
      }
    },
    "valueObject": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "id",
        "label",
        "domainRequirementRefs",
        "properties"
      ],
      "properties": {
        "id": {
          "$ref": "#/$defs/identifier"
        },
        "label": {
          "type": "string",
          "minLength": 1
        },
        "description": {
          "type": "string"
        },
        "domainRequirementRefs": {
          "$ref": "#/$defs/elementRequirementRefs"
        },
        "properties": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/property"
          }
        }
      }
    },
    "association": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "id",
        "label",
        "domainRequirementRefs",
        "sourceRef",
        "targetRef"
      ],
      "properties": {
        "id": {
          "$ref": "#/$defs/identifier"
        },
        "label": {
          "type": "string",
          "minLength": 1
        },
        "description": {
          "type": "string"
        },
        "domainRequirementRefs": {
          "$ref": "#/$defs/elementRequirementRefs"
        },
        "sourceRef": {
          "$ref": "#/$defs/identifier"
        },
        "targetRef": {
          "$ref": "#/$defs/identifier"
        }
      }
    },
    "domainOperation": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "id",
        "label",
        "domainRequirementRefs",
        "actsOnRefs",
        "preconditions",
        "postconditions"
      ],
      "properties": {
        "id": {
          "$ref": "#/$defs/identifier"
        },
        "label": {
          "type": "string",
          "minLength": 1
        },
        "description": {
          "type": "string"
        },
        "domainRequirementRefs": {
          "$ref": "#/$defs/elementRequirementRefs"
        },
        "actsOnRefs": {
          "$ref": "#/$defs/referenceArray"
        },
        "preconditions": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "postconditions": {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      }
    },
    "invariant": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "id",
        "label",
        "domainRequirementRefs",
        "appliesToRefs",
        "assertion"
      ],
      "properties": {
        "id": {
          "$ref": "#/$defs/identifier"
        },
        "label": {
          "type": "string",
          "minLength": 1
        },
        "description": {
          "type": "string"
        },
        "domainRequirementRefs": {
          "$ref": "#/$defs/elementRequirementRefs"
        },
        "appliesToRefs": {
          "$ref": "#/$defs/referenceArray"
        },
        "assertion": {
          "type": "string",
          "minLength": 1
        }
      }
    }
  }
}

9. Examples

9.1. Workshop Waitlist Domain Model

This example attaches a Domain Model payload to a UJGDocument. The example assumes the referenced Domain Requirement nodes are present in the containing document or resolvable through normal UJG imports.

json
{
  "@context": [
    "https://ujg.specs.openuji.org/ed/ns/context.jsonld",
    "https://ujg.specs.openuji.org/ed/ns/domain-requirements.context.jsonld"
  ],
  "@id": "https://example.com/ujg/workshop-waitlist.jsonld",
  "@type": "UJGDocument",
  "extensions": {
    "org.openuji.domain-model": {
      "formatVersion": "0.1",
      "id": "urn:domain:model:workshop-waitlist",
      "domainRequirementRefs": [
        "urn:ujg:domain-requirement:waitlisted-participation",
        "urn:ujg:domain-requirement:offer-validity",
        "urn:ujg:domain-requirement:accept-offer"
      ],
      "entities": [
        {
          "id": "urn:domain:entity:participant",
          "label": "Participant",
          "domainRequirementRefs": [
            "urn:ujg:domain-requirement:waitlisted-participation",
            "urn:ujg:domain-requirement:accept-offer"
          ],
          "properties": [
            {
              "id": "urn:domain:property:participation-status",
              "label": "Participation status",
              "domainRequirementRefs": [
                "urn:ujg:domain-requirement:waitlisted-participation",
                "urn:ujg:domain-requirement:accept-offer"
              ],
              "valueType": "string",
              "allowedValues": [
                "waitlisted",
                "confirmed"
              ]
            }
          ]
        },
        {
          "id": "urn:domain:entity:offer",
          "label": "Offer",
          "domainRequirementRefs": [
            "urn:ujg:domain-requirement:offer-validity",
            "urn:ujg:domain-requirement:accept-offer"
          ],
          "properties": [
            {
              "id": "urn:domain:property:offer-expires-at",
              "label": "Offer expires at",
              "domainRequirementRefs": [
                "urn:ujg:domain-requirement:offer-validity"
              ],
              "valueType": "datetime"
            }
          ]
        }
      ],
      "valueObjects": [],
      "associations": [
        {
          "id": "urn:domain:association:offer-participant",
          "label": "Offer participant",
          "domainRequirementRefs": [
            "urn:ujg:domain-requirement:accept-offer"
          ],
          "sourceRef": "urn:domain:entity:offer",
          "targetRef": "urn:domain:entity:participant"
        }
      ],
      "domainOperations": [
        {
          "id": "urn:domain:operation:accept-offer",
          "label": "Accept offer",
          "domainRequirementRefs": [
            "urn:ujg:domain-requirement:accept-offer"
          ],
          "actsOnRefs": [
            "urn:domain:entity:offer",
            "urn:domain:entity:participant"
          ],
          "preconditions": [
            "The offer is currently valid."
          ],
          "postconditions": [
            "The offer is consumed and the participant is confirmed."
          ]
        }
      ],
      "invariants": [
        {
          "id": "urn:domain:invariant:accepted-offer-consumed",
          "label": "Accepted offer is consumed",
          "domainRequirementRefs": [
            "urn:ujg:domain-requirement:accept-offer"
          ],
          "appliesToRefs": [
            "urn:domain:entity:offer",
            "urn:domain:operation:accept-offer"
          ],
          "assertion": "An accepted offer is no longer outstanding."
        }
      ]
    }
  },
  "nodes": [
    {
      "@type": "StateDomainRequirement",
      "@id": "urn:ujg:domain-requirement:waitlisted-participation",
      "label": "Waitlisted participation is distinguishable",
      "stateRef": "urn:ujg:state:waitlisted",
      "requirement": "The implementation must preserve that the participant is currently waiting for a place in the workshop."
    },
    {
      "@type": "ConditionDomainRequirement",
      "@id": "urn:ujg:domain-requirement:offer-validity",
      "label": "Offer validity can be determined",
      "conditionRef": "urn:ujg:condition:offer-valid",
      "requirement": "The implementation must be able to determine whether the outstanding workshop offer is currently valid."
    },
    {
      "@type": "EffectDomainRequirement",
      "@id": "urn:ujg:domain-requirement:accept-offer",
      "label": "Accepted offer confirms participation",
      "effectRef": "urn:ujg:effect:accept-offer",
      "requirement": "Accepting the outstanding offer must consume that offer and establish confirmed workshop participation."
    }
  ]
}
{
  "@context": [
    "https://ujg.specs.openuji.org/ed/ns/context.jsonld",
    "https://ujg.specs.openuji.org/ed/ns/domain-requirements.context.jsonld"
  ],
  "@id": "https://example.com/ujg/workshop-waitlist.jsonld",
  "@type": "UJGDocument",
  "extensions": {
    "org.openuji.domain-model": {
      "formatVersion": "0.1",
      "id": "urn:domain:model:workshop-waitlist",
      "domainRequirementRefs": [
        "urn:ujg:domain-requirement:waitlisted-participation",
        "urn:ujg:domain-requirement:offer-validity",
        "urn:ujg:domain-requirement:accept-offer"
      ],
      "entities": [
        {
          "id": "urn:domain:entity:participant",
          "label": "Participant",
          "domainRequirementRefs": [
            "urn:ujg:domain-requirement:waitlisted-participation",
            "urn:ujg:domain-requirement:accept-offer"
          ],
          "properties": [
            {
              "id": "urn:domain:property:participation-status",
              "label": "Participation status",
              "domainRequirementRefs": [
                "urn:ujg:domain-requirement:waitlisted-participation",
                "urn:ujg:domain-requirement:accept-offer"
              ],
              "valueType": "string",
              "allowedValues": [
                "waitlisted",
                "confirmed"
              ]
            }
          ]
        },
        {
          "id": "urn:domain:entity:offer",
          "label": "Offer",
          "domainRequirementRefs": [
            "urn:ujg:domain-requirement:offer-validity",
            "urn:ujg:domain-requirement:accept-offer"
          ],
          "properties": [
            {
              "id": "urn:domain:property:offer-expires-at",
              "label": "Offer expires at",
              "domainRequirementRefs": [
                "urn:ujg:domain-requirement:offer-validity"
              ],
              "valueType": "datetime"
            }
          ]
        }
      ],
      "valueObjects": [],
      "associations": [
        {
          "id": "urn:domain:association:offer-participant",
          "label": "Offer participant",
          "domainRequirementRefs": [
            "urn:ujg:domain-requirement:accept-offer"
          ],
          "sourceRef": "urn:domain:entity:offer",
          "targetRef": "urn:domain:entity:participant"
        }
      ],
      "domainOperations": [
        {
          "id": "urn:domain:operation:accept-offer",
          "label": "Accept offer",
          "domainRequirementRefs": [
            "urn:ujg:domain-requirement:accept-offer"
          ],
          "actsOnRefs": [
            "urn:domain:entity:offer",
            "urn:domain:entity:participant"
          ],
          "preconditions": [
            "The offer is currently valid."
          ],
          "postconditions": [
            "The offer is consumed and the participant is confirmed."
          ]
        }
      ],
      "invariants": [
        {
          "id": "urn:domain:invariant:accepted-offer-consumed",
          "label": "Accepted offer is consumed",
          "domainRequirementRefs": [
            "urn:ujg:domain-requirement:accept-offer"
          ],
          "appliesToRefs": [
            "urn:domain:entity:offer",
            "urn:domain:operation:accept-offer"
          ],
          "assertion": "An accepted offer is no longer outstanding."
        }
      ]
    }
  },
  "nodes": [
    {
      "@type": "StateDomainRequirement",
      "@id": "urn:ujg:domain-requirement:waitlisted-participation",
      "label": "Waitlisted participation is distinguishable",
      "stateRef": "urn:ujg:state:waitlisted",
      "requirement": "The implementation must preserve that the participant is currently waiting for a place in the workshop."
    },
    {
      "@type": "ConditionDomainRequirement",
      "@id": "urn:ujg:domain-requirement:offer-validity",
      "label": "Offer validity can be determined",
      "conditionRef": "urn:ujg:condition:offer-valid",
      "requirement": "The implementation must be able to determine whether the outstanding workshop offer is currently valid."
    },
    {
      "@type": "EffectDomainRequirement",
      "@id": "urn:ujg:domain-requirement:accept-offer",
      "label": "Accepted offer confirms participation",
      "effectRef": "urn:ujg:effect:accept-offer",
      "requirement": "Accepting the outstanding offer must consume that offer and establish confirmed workshop participation."
    }
  ]
}

Copyright © 2026 the Contributors to the ujg/extensions/domain-model, published by the User Journal Graph Community Group under the W3C Community Contributor License Agreement (CLA) . A human-readable summary is available.

generated by Speculator