Domain Requirements
draftStatus 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 .
1. Overview
Domain Requirements is a second-level optional module for declaring implementation-domain obligations anchored to existing UJG semantics. It does not define domain models or implementation architecture.
UJG describes user-observable journey semantics. First-level optional modules can add semantics such as transition conditions, transition effects, and opaque entry bindings. Domain Requirements makes the implementation-domain obligations implied by those semantic sources explicit and machine-addressable without changing the source modules.
This module is intentionally narrow. A DomainRequirement says that an implementation domain must support some capability because of one specific UJG semantic source. It does not describe how that capability is implemented.
Consumers that do not implement this module MAY ignore Domain Requirement nodes. Ignoring this module MUST NOT change Graph topology, Graph traversal, Condition semantics, Effect semantics, Entry Binding semantics, Surface semantics, Runtime, or Mapping.
2. Non-goals and Future Considerations
This module does not define a domain model. It MUST NOT define vocabulary for entities, aggregates, value objects, fields, database schemas, tables, primary keys, persistence, API endpoints, HTTP, RPC, commands, domain events, repositories, services, transport, transactions, locks, queues, authentication mechanisms, authorization mechanisms, sessions, OAuth/OIDC, ORM mappings, framework concepts, or programming-language types.
Cross-node invariants are deliberately out of scope for this first version. A future module may define additional vocabulary for invariants or multi-source requirements.
3. Terminology
DomainRequirement: An abstract superclass for implementation-domain obligations anchored to UJG semantic sources.
StateDomainRequirement: A domain requirement anchored to one Graph
State.ConditionDomainRequirement: A domain requirement anchored to one
Condition.EffectDomainRequirement: A domain requirement anchored to one
Effect.EntryBindingDomainRequirement: A domain requirement anchored to one
EntryBinding.Requirement source: The single UJG node whose semantics justify a concrete domain requirement.
4. DomainRequirement
DomainRequirement is the common abstract superclass for concrete requirements. It exists for ontology organization and generic querying.
A DomainRequirement MUST NOT be used as the sole concrete type of a requirement node. Concrete requirements MUST use exactly one of the concrete classes defined by this module.
The requirement property contains the human-readable domain obligation. It describes what the
implementation domain must support and MUST remain technology-neutral.
A concrete Domain Requirement MUST be identified by an IRI.
A concrete Domain Requirement MUST declare exactly one
requirement.The
requirementvalue MUST be anxsd:string.The
requirementvalue MUST remain technology-neutral.DomainRequirementMUST NOT be used as the sole concrete requirement type.A concrete Domain Requirement MUST NOT use more than one concrete Domain Requirements subclass.
5. StateDomainRequirement
A StateDomainRequirement identifies a domain capability necessary to support the semantics of
one Graph State. It does not mean that a UJG State is a domain state.
A StateDomainRequirement MUST declare exactly one
stateRef.The
stateRefvalue MUST be an IRI.The
stateRefvalue MUST reference a GraphState, including valid subclasses such asCompositeState.A StateDomainRequirement MUST NOT declare
conditionRef,effectRef, orentryBindingRef.A StateDomainRequirement SHOULD add domain semantics not already completely represented by UJG topology.
Example JSON node:
{
"@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": "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."
} 6. ConditionDomainRequirement
A ConditionDomainRequirement identifies domain information or capability necessary to determine
one existing UJG Condition.
A ConditionDomainRequirement MUST declare exactly one
conditionRef.The
conditionRefvalue MUST be an IRI.The
conditionRefvalue MUST reference aConditionfrom the Conditions module.A ConditionDomainRequirement MUST NOT declare
stateRef,effectRef, orentryBindingRef.A ConditionDomainRequirement MUST NOT define a condition expression language, evaluator API, database lookup, implementation field, or runtime execution semantics.
Example JSON node:
{
"@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": "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."
} 7. EffectDomainRequirement
An EffectDomainRequirement identifies a domain consequence or state-changing capability required
to realize one existing UJG Effect.
An EffectDomainRequirement MUST declare exactly one
effectRef.The
effectRefvalue MUST be an IRI.The
effectRefvalue MUST reference anEffectfrom the Effect module.An EffectDomainRequirement MUST NOT declare
stateRef,conditionRef, orentryBindingRef.An EffectDomainRequirement MUST NOT define API calls, HTTP methods, commands, database updates, transactions, retries, idempotency implementation, or service boundaries.
Example JSON node:
{
"@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."
} {
"@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."
} 8. EntryBindingDomainRequirement
An EntryBindingDomainRequirement identifies domain identity or continuity semantics required
when resolving one existing EntryBinding.
An EntryBindingDomainRequirement MUST declare exactly one
entryBindingRef.The
entryBindingRefvalue MUST be an IRI.The
entryBindingRefvalue MUST reference anEntryBindingfrom the Entry Binding module.An EntryBindingDomainRequirement MUST NOT declare
stateRef,conditionRef, oreffectRef.An EntryBindingDomainRequirement MUST NOT define accounts, login, sessions, JWT, OAuth, cookies, URL tokens, database identifiers, or any other identity implementation.
Example JSON node:
{
"@type": "EntryBindingDomainRequirement",
"@id": "urn:ujg:domain-requirement:spot-offer-continuity",
"label": "Offer identity survives touchpoint change",
"entryBindingRef": "urn:ujg:entry-binding:spot-offer",
"requirement": "The external application entry must resolve the same outstanding participant-specific offer represented by the notification."
} {
"@type": "EntryBindingDomainRequirement",
"@id": "urn:ujg:domain-requirement:spot-offer-continuity",
"label": "Offer identity survives touchpoint change",
"entryBindingRef": "urn:ujg:entry-binding:spot-offer",
"requirement": "The external application entry must resolve the same outstanding participant-specific offer represented by the notification."
} 9. Requirement Model and Processing Rules
Each concrete requirement is anchored to exactly one semantic source:
StateDomainRequirementusesstateRef.ConditionDomainRequirementusesconditionRef.EffectDomainRequirementuseseffectRef.EntryBindingDomainRequirementusesentryBindingRef.
If several aspects of UJG jointly imply domain semantics, authors SHOULD express the necessary requirements independently against their respective semantic sources. The existing UJG topology already relates states, transitions, conditions, effects, and entries. Domain Requirements MUST NOT create a second graph of those relationships.
Traceability is assertion, not proof. A reference property asserts that a requirement is justified by its UJG semantic source. This module does not standardize a derivation algorithm. Requirements may be human-authored, AI-derived, tool-generated, or imported from another requirements workflow.
A Domain Requirement is a declarative downstream obligation. It MUST NOT create a Graph edge, change
from or to, create a State, create a Transition, make a Transition eligible, evaluate a
Condition, execute an Effect, resolve an EntryBinding, alter Runtime ordering, create
RuntimeEvents, change Mapping, imply navigation, or imply execution order.
Domain Requirements SHOULD add domain semantics that are not already completely represented by UJG topology itself. A requirement such as "The implementation must allow the user to move from Offer to Confirmed" is redundant when Graph already represents that transition.
A consumer that does not implement Domain Requirements semantics MAY ignore Domain Requirement nodes. Such a consumer SHOULD preserve recognized JSON-LD data during read-transform-write when possible.
10. Normative Artifacts
This module is published through the following artifacts:
domain-requirements.ttl: ontology, published athttps://ujg.specs.openuji.org/ed/ns/domain-requirementsdomain-requirements.context.jsonld: JSON-LD term mappings, published athttps://ujg.specs.openuji.org/ed/ns/domain-requirements.context.jsonlddomain-requirements.shape.ttl: SHACL validation rules, published athttps://ujg.specs.openuji.org/ed/ns/domain-requirements.shape
Examples in this page compose the shared baseline context with the Conditions, Effect, Entry Binding, and Domain Requirements contexts:
[
"https://ujg.specs.openuji.org/ed/ns/context.jsonld",
"https://ujg.specs.openuji.org/ed/ns/condition.context.jsonld",
"https://ujg.specs.openuji.org/ed/ns/effect.context.jsonld",
"https://ujg.specs.openuji.org/ed/ns/entry-binding.context.jsonld",
"https://ujg.specs.openuji.org/ed/ns/domain-requirements.context.jsonld"
] [
"https://ujg.specs.openuji.org/ed/ns/context.jsonld",
"https://ujg.specs.openuji.org/ed/ns/condition.context.jsonld",
"https://ujg.specs.openuji.org/ed/ns/effect.context.jsonld",
"https://ujg.specs.openuji.org/ed/ns/entry-binding.context.jsonld",
"https://ujg.specs.openuji.org/ed/ns/domain-requirements.context.jsonld"
] 10.1. Ontology
The normative Domain Requirements ontology is defined below and is published at
https://ujg.specs.openuji.org/ed/ns/domain-requirements.
@prefix ujg: <https://ujg.specs.openuji.org/ed/ns/core#> .
@prefix ujggraph: <https://ujg.specs.openuji.org/ed/ns/graph#> .
@prefix ujgcond: <https://ujg.specs.openuji.org/ed/ns/condition#> .
@prefix ujgeffect: <https://ujg.specs.openuji.org/ed/ns/effect#> .
@prefix ujgentrybinding: <https://ujg.specs.openuji.org/ed/ns/entry-binding#> .
@prefix ujgdomainrequirements: <https://ujg.specs.openuji.org/ed/ns/domain-requirements#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix dct: <http://purl.org/dc/terms/> .
<https://ujg.specs.openuji.org/ed/ns/domain-requirements#> a owl:Ontology ;
rdfs:label "UJG Domain Requirements Editor's Draft Vocabulary"@en ;
dct:description "UJG Domain Requirements ontology declaration" .
### Classes
ujgdomainrequirements:DomainRequirement a owl:Class ;
rdfs:subClassOf ujg:Node ;
rdfs:label "Domain Requirement" ;
rdfs:comment "An abstract superclass for implementation-domain obligations anchored to UJG semantic sources." .
ujgdomainrequirements:StateDomainRequirement a owl:Class ;
rdfs:subClassOf ujgdomainrequirements:DomainRequirement .
ujgdomainrequirements:ConditionDomainRequirement a owl:Class ;
rdfs:subClassOf ujgdomainrequirements:DomainRequirement .
ujgdomainrequirements:EffectDomainRequirement a owl:Class ;
rdfs:subClassOf ujgdomainrequirements:DomainRequirement .
ujgdomainrequirements:EntryBindingDomainRequirement a owl:Class ;
rdfs:subClassOf ujgdomainrequirements:DomainRequirement .
### Properties
ujgdomainrequirements:requirement a owl:DatatypeProperty ;
rdfs:domain ujgdomainrequirements:DomainRequirement ;
rdfs:range xsd:string ;
rdfs:label "requirement" ;
rdfs:comment "A human-readable, technology-neutral obligation that the implementation domain must support." .
ujgdomainrequirements:stateRef a owl:ObjectProperty ;
rdfs:domain ujgdomainrequirements:StateDomainRequirement ;
rdfs:range ujggraph:State ;
rdfs:label "state ref" ;
rdfs:comment "References the Graph State whose user-journey semantics justify this domain requirement." .
ujgdomainrequirements:conditionRef a owl:ObjectProperty ;
rdfs:domain ujgdomainrequirements:ConditionDomainRequirement ;
rdfs:range ujgcond:Condition ;
rdfs:label "condition ref" ;
rdfs:comment "References the Condition whose determinability justifies this domain requirement." .
ujgdomainrequirements:effectRef a owl:ObjectProperty ;
rdfs:domain ujgdomainrequirements:EffectDomainRequirement ;
rdfs:range ujgeffect:Effect ;
rdfs:label "effect ref" ;
rdfs:comment "References the Effect whose realization justifies this domain requirement." .
ujgdomainrequirements:entryBindingRef a owl:ObjectProperty ;
rdfs:domain ujgdomainrequirements:EntryBindingDomainRequirement ;
rdfs:range ujgentrybinding:EntryBinding ;
rdfs:label "entry binding ref" ;
rdfs:comment "References the EntryBinding whose resolution semantics justify this domain requirement." . @prefix ujg: <https://ujg.specs.openuji.org/ed/ns/core#> .
@prefix ujggraph: <https://ujg.specs.openuji.org/ed/ns/graph#> .
@prefix ujgcond: <https://ujg.specs.openuji.org/ed/ns/condition#> .
@prefix ujgeffect: <https://ujg.specs.openuji.org/ed/ns/effect#> .
@prefix ujgentrybinding: <https://ujg.specs.openuji.org/ed/ns/entry-binding#> .
@prefix ujgdomainrequirements: <https://ujg.specs.openuji.org/ed/ns/domain-requirements#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix dct: <http://purl.org/dc/terms/> .
<https://ujg.specs.openuji.org/ed/ns/domain-requirements#> a owl:Ontology ;
rdfs:label "UJG Domain Requirements Editor's Draft Vocabulary"@en ;
dct:description "UJG Domain Requirements ontology declaration" .
### Classes
ujgdomainrequirements:DomainRequirement a owl:Class ;
rdfs:subClassOf ujg:Node ;
rdfs:label "Domain Requirement" ;
rdfs:comment "An abstract superclass for implementation-domain obligations anchored to UJG semantic sources." .
ujgdomainrequirements:StateDomainRequirement a owl:Class ;
rdfs:subClassOf ujgdomainrequirements:DomainRequirement .
ujgdomainrequirements:ConditionDomainRequirement a owl:Class ;
rdfs:subClassOf ujgdomainrequirements:DomainRequirement .
ujgdomainrequirements:EffectDomainRequirement a owl:Class ;
rdfs:subClassOf ujgdomainrequirements:DomainRequirement .
ujgdomainrequirements:EntryBindingDomainRequirement a owl:Class ;
rdfs:subClassOf ujgdomainrequirements:DomainRequirement .
### Properties
ujgdomainrequirements:requirement a owl:DatatypeProperty ;
rdfs:domain ujgdomainrequirements:DomainRequirement ;
rdfs:range xsd:string ;
rdfs:label "requirement" ;
rdfs:comment "A human-readable, technology-neutral obligation that the implementation domain must support." .
ujgdomainrequirements:stateRef a owl:ObjectProperty ;
rdfs:domain ujgdomainrequirements:StateDomainRequirement ;
rdfs:range ujggraph:State ;
rdfs:label "state ref" ;
rdfs:comment "References the Graph State whose user-journey semantics justify this domain requirement." .
ujgdomainrequirements:conditionRef a owl:ObjectProperty ;
rdfs:domain ujgdomainrequirements:ConditionDomainRequirement ;
rdfs:range ujgcond:Condition ;
rdfs:label "condition ref" ;
rdfs:comment "References the Condition whose determinability justifies this domain requirement." .
ujgdomainrequirements:effectRef a owl:ObjectProperty ;
rdfs:domain ujgdomainrequirements:EffectDomainRequirement ;
rdfs:range ujgeffect:Effect ;
rdfs:label "effect ref" ;
rdfs:comment "References the Effect whose realization justifies this domain requirement." .
ujgdomainrequirements:entryBindingRef a owl:ObjectProperty ;
rdfs:domain ujgdomainrequirements:EntryBindingDomainRequirement ;
rdfs:range ujgentrybinding:EntryBinding ;
rdfs:label "entry binding ref" ;
rdfs:comment "References the EntryBinding whose resolution semantics justify this domain requirement." . 10.2. JSON-LD Context
The normative Domain Requirements JSON-LD context is defined below and is published at
https://ujg.specs.openuji.org/ed/ns/domain-requirements.context.jsonld.
{
"@context": {
"@version": 1.1,
"ujgdomainrequirements": "https://ujg.specs.openuji.org/ed/ns/domain-requirements#",
"domainrequirements": "https://ujg.specs.openuji.org/ed/ns/domain-requirements#",
"DomainRequirement": "ujgdomainrequirements:DomainRequirement",
"StateDomainRequirement": {
"@id": "ujgdomainrequirements:StateDomainRequirement",
"@context": {
"stateRef": {
"@id": "ujgdomainrequirements:stateRef",
"@type": "@id"
}
}
},
"ConditionDomainRequirement": {
"@id": "ujgdomainrequirements:ConditionDomainRequirement",
"@context": {
"conditionRef": {
"@id": "ujgdomainrequirements:conditionRef",
"@type": "@id"
}
}
},
"EffectDomainRequirement": {
"@id": "ujgdomainrequirements:EffectDomainRequirement",
"@context": {
"effectRef": {
"@id": "ujgdomainrequirements:effectRef",
"@type": "@id"
}
}
},
"EntryBindingDomainRequirement": "ujgdomainrequirements:EntryBindingDomainRequirement",
"requirement": "ujgdomainrequirements:requirement",
"entryBindingRef": {
"@id": "ujgdomainrequirements:entryBindingRef",
"@type": "@id"
}
}
} {
"@context": {
"@version": 1.1,
"ujgdomainrequirements": "https://ujg.specs.openuji.org/ed/ns/domain-requirements#",
"domainrequirements": "https://ujg.specs.openuji.org/ed/ns/domain-requirements#",
"DomainRequirement": "ujgdomainrequirements:DomainRequirement",
"StateDomainRequirement": {
"@id": "ujgdomainrequirements:StateDomainRequirement",
"@context": {
"stateRef": {
"@id": "ujgdomainrequirements:stateRef",
"@type": "@id"
}
}
},
"ConditionDomainRequirement": {
"@id": "ujgdomainrequirements:ConditionDomainRequirement",
"@context": {
"conditionRef": {
"@id": "ujgdomainrequirements:conditionRef",
"@type": "@id"
}
}
},
"EffectDomainRequirement": {
"@id": "ujgdomainrequirements:EffectDomainRequirement",
"@context": {
"effectRef": {
"@id": "ujgdomainrequirements:effectRef",
"@type": "@id"
}
}
},
"EntryBindingDomainRequirement": "ujgdomainrequirements:EntryBindingDomainRequirement",
"requirement": "ujgdomainrequirements:requirement",
"entryBindingRef": {
"@id": "ujgdomainrequirements:entryBindingRef",
"@type": "@id"
}
}
} 10.3. Validation
The normative Domain Requirements SHACL shape is defined below and is published at
https://ujg.specs.openuji.org/ed/ns/domain-requirements.shape.
@prefix ujgdomainrequirements: <https://ujg.specs.openuji.org/ed/ns/domain-requirements#> .
@prefix ujgdomainrequirementsshape: <https://ujg.specs.openuji.org/ed/ns/domain-requirements.shape#> .
@prefix ujggraph: <https://ujg.specs.openuji.org/ed/ns/graph#> .
@prefix ujgcond: <https://ujg.specs.openuji.org/ed/ns/condition#> .
@prefix ujgeffect: <https://ujg.specs.openuji.org/ed/ns/effect#> .
@prefix ujgentrybinding: <https://ujg.specs.openuji.org/ed/ns/entry-binding#> .
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
ujgdomainrequirementsshape:DomainRequirementShape a sh:NodeShape ;
sh:targetClass ujgdomainrequirements:DomainRequirement ;
sh:nodeKind sh:IRI ;
sh:property [
sh:path ujgdomainrequirements:requirement ;
sh:datatype xsd:string ;
sh:minCount 1 ;
sh:maxCount 1 ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "DomainRequirement MUST NOT be used as the sole concrete requirement type." ;
sh:select """
SELECT $this
WHERE {
FILTER (
NOT EXISTS { $this a ujgdomainrequirements:StateDomainRequirement . }
&& NOT EXISTS { $this a ujgdomainrequirements:ConditionDomainRequirement . }
&& NOT EXISTS { $this a ujgdomainrequirements:EffectDomainRequirement . }
&& NOT EXISTS { $this a ujgdomainrequirements:EntryBindingDomainRequirement . }
)
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A Domain Requirement MUST use at most one concrete Domain Requirements subclass." ;
sh:select """
SELECT $this
WHERE {
{
SELECT $this (COUNT(?type) AS ?typeCount)
WHERE {
VALUES ?type {
ujgdomainrequirements:StateDomainRequirement
ujgdomainrequirements:ConditionDomainRequirement
ujgdomainrequirements:EffectDomainRequirement
ujgdomainrequirements:EntryBindingDomainRequirement
}
$this a ?type .
}
GROUP BY $this
}
FILTER (?typeCount > 1)
}
""" ;
] .
ujgdomainrequirementsshape:StateDomainRequirementShape a sh:NodeShape ;
sh:targetClass ujgdomainrequirements:StateDomainRequirement ;
sh:nodeKind sh:IRI ;
sh:property [
sh:path ujgdomainrequirements:requirement ;
sh:datatype xsd:string ;
sh:minCount 1 ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ujgdomainrequirements:stateRef ;
sh:class ujggraph:State ;
sh:nodeKind sh:IRI ;
sh:minCount 1 ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ujgdomainrequirements:conditionRef ;
sh:maxCount 0 ;
] ;
sh:property [
sh:path ujgdomainrequirements:effectRef ;
sh:maxCount 0 ;
] ;
sh:property [
sh:path ujgdomainrequirements:entryBindingRef ;
sh:maxCount 0 ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "StateDomainRequirement MUST NOT use another concrete Domain Requirements subclass." ;
sh:select """
SELECT $this
WHERE {
FILTER (
EXISTS { $this a ujgdomainrequirements:ConditionDomainRequirement . }
|| EXISTS { $this a ujgdomainrequirements:EffectDomainRequirement . }
|| EXISTS { $this a ujgdomainrequirements:EntryBindingDomainRequirement . }
)
}
""" ;
] .
ujgdomainrequirementsshape:ConditionDomainRequirementShape a sh:NodeShape ;
sh:targetClass ujgdomainrequirements:ConditionDomainRequirement ;
sh:nodeKind sh:IRI ;
sh:property [
sh:path ujgdomainrequirements:requirement ;
sh:datatype xsd:string ;
sh:minCount 1 ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ujgdomainrequirements:conditionRef ;
sh:class ujgcond:Condition ;
sh:nodeKind sh:IRI ;
sh:minCount 1 ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ujgdomainrequirements:stateRef ;
sh:maxCount 0 ;
] ;
sh:property [
sh:path ujgdomainrequirements:effectRef ;
sh:maxCount 0 ;
] ;
sh:property [
sh:path ujgdomainrequirements:entryBindingRef ;
sh:maxCount 0 ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "ConditionDomainRequirement MUST NOT use another concrete Domain Requirements subclass." ;
sh:select """
SELECT $this
WHERE {
FILTER (
EXISTS { $this a ujgdomainrequirements:StateDomainRequirement . }
|| EXISTS { $this a ujgdomainrequirements:EffectDomainRequirement . }
|| EXISTS { $this a ujgdomainrequirements:EntryBindingDomainRequirement . }
)
}
""" ;
] .
ujgdomainrequirementsshape:EffectDomainRequirementShape a sh:NodeShape ;
sh:targetClass ujgdomainrequirements:EffectDomainRequirement ;
sh:nodeKind sh:IRI ;
sh:property [
sh:path ujgdomainrequirements:requirement ;
sh:datatype xsd:string ;
sh:minCount 1 ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ujgdomainrequirements:effectRef ;
sh:class ujgeffect:Effect ;
sh:nodeKind sh:IRI ;
sh:minCount 1 ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ujgdomainrequirements:stateRef ;
sh:maxCount 0 ;
] ;
sh:property [
sh:path ujgdomainrequirements:conditionRef ;
sh:maxCount 0 ;
] ;
sh:property [
sh:path ujgdomainrequirements:entryBindingRef ;
sh:maxCount 0 ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "EffectDomainRequirement MUST NOT use another concrete Domain Requirements subclass." ;
sh:select """
SELECT $this
WHERE {
FILTER (
EXISTS { $this a ujgdomainrequirements:StateDomainRequirement . }
|| EXISTS { $this a ujgdomainrequirements:ConditionDomainRequirement . }
|| EXISTS { $this a ujgdomainrequirements:EntryBindingDomainRequirement . }
)
}
""" ;
] .
ujgdomainrequirementsshape:EntryBindingDomainRequirementShape a sh:NodeShape ;
sh:targetClass ujgdomainrequirements:EntryBindingDomainRequirement ;
sh:nodeKind sh:IRI ;
sh:property [
sh:path ujgdomainrequirements:requirement ;
sh:datatype xsd:string ;
sh:minCount 1 ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ujgdomainrequirements:entryBindingRef ;
sh:class ujgentrybinding:EntryBinding ;
sh:nodeKind sh:IRI ;
sh:minCount 1 ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ujgdomainrequirements:stateRef ;
sh:maxCount 0 ;
] ;
sh:property [
sh:path ujgdomainrequirements:conditionRef ;
sh:maxCount 0 ;
] ;
sh:property [
sh:path ujgdomainrequirements:effectRef ;
sh:maxCount 0 ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "EntryBindingDomainRequirement MUST NOT use another concrete Domain Requirements subclass." ;
sh:select """
SELECT $this
WHERE {
FILTER (
EXISTS { $this a ujgdomainrequirements:StateDomainRequirement . }
|| EXISTS { $this a ujgdomainrequirements:ConditionDomainRequirement . }
|| EXISTS { $this a ujgdomainrequirements:EffectDomainRequirement . }
)
}
""" ;
] . @prefix ujgdomainrequirements: <https://ujg.specs.openuji.org/ed/ns/domain-requirements#> .
@prefix ujgdomainrequirementsshape: <https://ujg.specs.openuji.org/ed/ns/domain-requirements.shape#> .
@prefix ujggraph: <https://ujg.specs.openuji.org/ed/ns/graph#> .
@prefix ujgcond: <https://ujg.specs.openuji.org/ed/ns/condition#> .
@prefix ujgeffect: <https://ujg.specs.openuji.org/ed/ns/effect#> .
@prefix ujgentrybinding: <https://ujg.specs.openuji.org/ed/ns/entry-binding#> .
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
ujgdomainrequirementsshape:DomainRequirementShape a sh:NodeShape ;
sh:targetClass ujgdomainrequirements:DomainRequirement ;
sh:nodeKind sh:IRI ;
sh:property [
sh:path ujgdomainrequirements:requirement ;
sh:datatype xsd:string ;
sh:minCount 1 ;
sh:maxCount 1 ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "DomainRequirement MUST NOT be used as the sole concrete requirement type." ;
sh:select """
SELECT $this
WHERE {
FILTER (
NOT EXISTS { $this a ujgdomainrequirements:StateDomainRequirement . }
&& NOT EXISTS { $this a ujgdomainrequirements:ConditionDomainRequirement . }
&& NOT EXISTS { $this a ujgdomainrequirements:EffectDomainRequirement . }
&& NOT EXISTS { $this a ujgdomainrequirements:EntryBindingDomainRequirement . }
)
}
""" ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A Domain Requirement MUST use at most one concrete Domain Requirements subclass." ;
sh:select """
SELECT $this
WHERE {
{
SELECT $this (COUNT(?type) AS ?typeCount)
WHERE {
VALUES ?type {
ujgdomainrequirements:StateDomainRequirement
ujgdomainrequirements:ConditionDomainRequirement
ujgdomainrequirements:EffectDomainRequirement
ujgdomainrequirements:EntryBindingDomainRequirement
}
$this a ?type .
}
GROUP BY $this
}
FILTER (?typeCount > 1)
}
""" ;
] .
ujgdomainrequirementsshape:StateDomainRequirementShape a sh:NodeShape ;
sh:targetClass ujgdomainrequirements:StateDomainRequirement ;
sh:nodeKind sh:IRI ;
sh:property [
sh:path ujgdomainrequirements:requirement ;
sh:datatype xsd:string ;
sh:minCount 1 ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ujgdomainrequirements:stateRef ;
sh:class ujggraph:State ;
sh:nodeKind sh:IRI ;
sh:minCount 1 ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ujgdomainrequirements:conditionRef ;
sh:maxCount 0 ;
] ;
sh:property [
sh:path ujgdomainrequirements:effectRef ;
sh:maxCount 0 ;
] ;
sh:property [
sh:path ujgdomainrequirements:entryBindingRef ;
sh:maxCount 0 ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "StateDomainRequirement MUST NOT use another concrete Domain Requirements subclass." ;
sh:select """
SELECT $this
WHERE {
FILTER (
EXISTS { $this a ujgdomainrequirements:ConditionDomainRequirement . }
|| EXISTS { $this a ujgdomainrequirements:EffectDomainRequirement . }
|| EXISTS { $this a ujgdomainrequirements:EntryBindingDomainRequirement . }
)
}
""" ;
] .
ujgdomainrequirementsshape:ConditionDomainRequirementShape a sh:NodeShape ;
sh:targetClass ujgdomainrequirements:ConditionDomainRequirement ;
sh:nodeKind sh:IRI ;
sh:property [
sh:path ujgdomainrequirements:requirement ;
sh:datatype xsd:string ;
sh:minCount 1 ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ujgdomainrequirements:conditionRef ;
sh:class ujgcond:Condition ;
sh:nodeKind sh:IRI ;
sh:minCount 1 ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ujgdomainrequirements:stateRef ;
sh:maxCount 0 ;
] ;
sh:property [
sh:path ujgdomainrequirements:effectRef ;
sh:maxCount 0 ;
] ;
sh:property [
sh:path ujgdomainrequirements:entryBindingRef ;
sh:maxCount 0 ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "ConditionDomainRequirement MUST NOT use another concrete Domain Requirements subclass." ;
sh:select """
SELECT $this
WHERE {
FILTER (
EXISTS { $this a ujgdomainrequirements:StateDomainRequirement . }
|| EXISTS { $this a ujgdomainrequirements:EffectDomainRequirement . }
|| EXISTS { $this a ujgdomainrequirements:EntryBindingDomainRequirement . }
)
}
""" ;
] .
ujgdomainrequirementsshape:EffectDomainRequirementShape a sh:NodeShape ;
sh:targetClass ujgdomainrequirements:EffectDomainRequirement ;
sh:nodeKind sh:IRI ;
sh:property [
sh:path ujgdomainrequirements:requirement ;
sh:datatype xsd:string ;
sh:minCount 1 ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ujgdomainrequirements:effectRef ;
sh:class ujgeffect:Effect ;
sh:nodeKind sh:IRI ;
sh:minCount 1 ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ujgdomainrequirements:stateRef ;
sh:maxCount 0 ;
] ;
sh:property [
sh:path ujgdomainrequirements:conditionRef ;
sh:maxCount 0 ;
] ;
sh:property [
sh:path ujgdomainrequirements:entryBindingRef ;
sh:maxCount 0 ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "EffectDomainRequirement MUST NOT use another concrete Domain Requirements subclass." ;
sh:select """
SELECT $this
WHERE {
FILTER (
EXISTS { $this a ujgdomainrequirements:StateDomainRequirement . }
|| EXISTS { $this a ujgdomainrequirements:ConditionDomainRequirement . }
|| EXISTS { $this a ujgdomainrequirements:EntryBindingDomainRequirement . }
)
}
""" ;
] .
ujgdomainrequirementsshape:EntryBindingDomainRequirementShape a sh:NodeShape ;
sh:targetClass ujgdomainrequirements:EntryBindingDomainRequirement ;
sh:nodeKind sh:IRI ;
sh:property [
sh:path ujgdomainrequirements:requirement ;
sh:datatype xsd:string ;
sh:minCount 1 ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ujgdomainrequirements:entryBindingRef ;
sh:class ujgentrybinding:EntryBinding ;
sh:nodeKind sh:IRI ;
sh:minCount 1 ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ujgdomainrequirements:stateRef ;
sh:maxCount 0 ;
] ;
sh:property [
sh:path ujgdomainrequirements:conditionRef ;
sh:maxCount 0 ;
] ;
sh:property [
sh:path ujgdomainrequirements:effectRef ;
sh:maxCount 0 ;
] ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "EntryBindingDomainRequirement MUST NOT use another concrete Domain Requirements subclass." ;
sh:select """
SELECT $this
WHERE {
FILTER (
EXISTS { $this a ujgdomainrequirements:StateDomainRequirement . }
|| EXISTS { $this a ujgdomainrequirements:ConditionDomainRequirement . }
|| EXISTS { $this a ujgdomainrequirements:EffectDomainRequirement . }
)
}
""" ;
] . The SHACL shape validates structure and source-reference classes. It does not validate whether the
natural-language requirement value is semantically correct.
11. Examples
11.1. Workshop Waitlist Example
This example shows a participant moving through a workshop waitlist flow. The Graph, Condition, Effect, and Entry Binding nodes retain their own semantics. Domain Requirement nodes only state the implementation-domain obligations needed to realize those semantics.
{
"@context": [
"https://ujg.specs.openuji.org/ed/ns/context.jsonld",
"https://ujg.specs.openuji.org/ed/ns/condition.context.jsonld",
"https://ujg.specs.openuji.org/ed/ns/effect.context.jsonld",
"https://ujg.specs.openuji.org/ed/ns/entry-binding.context.jsonld",
"https://ujg.specs.openuji.org/ed/ns/domain-requirements.context.jsonld"
],
"@id": "https://example.com/ujg/workshop-waitlist.jsonld",
"@type": "UJGDocument",
"nodes": [
{
"@type": "Journey",
"@id": "urn:ujg:journey:workshop-waitlist",
"defaultEntryRef": "urn:ujg:entry:workshop-waitlist-default",
"entryRefs": [
"urn:ujg:entry:workshop-waitlist-default",
"urn:ujg:entry:workshop-offer"
],
"stateRefs": [
"urn:ujg:state:waitlisted",
"urn:ujg:state:offer-available",
"urn:ujg:state:confirmed",
"urn:ujg:state:offer-expired"
],
"transitionRefs": [
"urn:ujg:transition:spot-available",
"urn:ujg:transition:accept-offer",
"urn:ujg:transition:decline-offer",
"urn:ujg:transition:expire-offer"
]
},
{
"@type": "JourneyEntry",
"@id": "urn:ujg:entry:workshop-waitlist-default",
"stateRef": "urn:ujg:state:waitlisted"
},
{
"@type": "JourneyEntry",
"@id": "urn:ujg:entry:workshop-offer",
"stateRef": "urn:ujg:state:offer-available"
},
{
"@type": "State",
"@id": "urn:ujg:state:waitlisted",
"label": "Waitlisted"
},
{
"@type": "State",
"@id": "urn:ujg:state:offer-available",
"label": "Offer available"
},
{
"@type": "State",
"@id": "urn:ujg:state:confirmed",
"label": "Confirmed"
},
{
"@type": "State",
"@id": "urn:ujg:state:offer-expired",
"label": "Offer expired"
},
{
"@type": "Transition",
"@id": "urn:ujg:transition:spot-available",
"from": "urn:ujg:state:waitlisted",
"to": "urn:ujg:state:offer-available",
"label": "Spot becomes available"
},
{
"@type": "Transition",
"@id": "urn:ujg:transition:accept-offer",
"from": "urn:ujg:state:offer-available",
"to": "urn:ujg:state:confirmed",
"label": "Accept",
"conditionRef": "urn:ujg:condition:offer-valid",
"effectRef": "urn:ujg:effect:accept-offer"
},
{
"@type": "Transition",
"@id": "urn:ujg:transition:decline-offer",
"from": "urn:ujg:state:offer-available",
"to": "urn:ujg:state:waitlisted",
"label": "Decline"
},
{
"@type": "Transition",
"@id": "urn:ujg:transition:expire-offer",
"from": "urn:ujg:state:offer-available",
"to": "urn:ujg:state:offer-expired",
"label": "Expire"
},
{
"@type": "Condition",
"@id": "urn:ujg:condition:offer-valid"
},
{
"@type": "Effect",
"@id": "urn:ujg:effect:accept-offer"
},
{
"@type": "EntryBinding",
"@id": "urn:ujg:entry-binding:spot-offer",
"entryRef": "urn:ujg:entry:workshop-offer",
"value": "workshop-offer-entry"
},
{
"@type": "StateDomainRequirement",
"@id": "urn:ujg:domain-requirement:waitlisted",
"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",
"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",
"effectRef": "urn:ujg:effect:accept-offer",
"requirement": "Accepting the outstanding offer must consume that offer and establish confirmed workshop participation."
},
{
"@type": "EntryBindingDomainRequirement",
"@id": "urn:ujg:domain-requirement:offer-continuity",
"entryBindingRef": "urn:ujg:entry-binding:spot-offer",
"requirement": "The external application entry must resolve the same outstanding participant-specific offer represented by the notification."
}
]
} {
"@context": [
"https://ujg.specs.openuji.org/ed/ns/context.jsonld",
"https://ujg.specs.openuji.org/ed/ns/condition.context.jsonld",
"https://ujg.specs.openuji.org/ed/ns/effect.context.jsonld",
"https://ujg.specs.openuji.org/ed/ns/entry-binding.context.jsonld",
"https://ujg.specs.openuji.org/ed/ns/domain-requirements.context.jsonld"
],
"@id": "https://example.com/ujg/workshop-waitlist.jsonld",
"@type": "UJGDocument",
"nodes": [
{
"@type": "Journey",
"@id": "urn:ujg:journey:workshop-waitlist",
"defaultEntryRef": "urn:ujg:entry:workshop-waitlist-default",
"entryRefs": [
"urn:ujg:entry:workshop-waitlist-default",
"urn:ujg:entry:workshop-offer"
],
"stateRefs": [
"urn:ujg:state:waitlisted",
"urn:ujg:state:offer-available",
"urn:ujg:state:confirmed",
"urn:ujg:state:offer-expired"
],
"transitionRefs": [
"urn:ujg:transition:spot-available",
"urn:ujg:transition:accept-offer",
"urn:ujg:transition:decline-offer",
"urn:ujg:transition:expire-offer"
]
},
{
"@type": "JourneyEntry",
"@id": "urn:ujg:entry:workshop-waitlist-default",
"stateRef": "urn:ujg:state:waitlisted"
},
{
"@type": "JourneyEntry",
"@id": "urn:ujg:entry:workshop-offer",
"stateRef": "urn:ujg:state:offer-available"
},
{
"@type": "State",
"@id": "urn:ujg:state:waitlisted",
"label": "Waitlisted"
},
{
"@type": "State",
"@id": "urn:ujg:state:offer-available",
"label": "Offer available"
},
{
"@type": "State",
"@id": "urn:ujg:state:confirmed",
"label": "Confirmed"
},
{
"@type": "State",
"@id": "urn:ujg:state:offer-expired",
"label": "Offer expired"
},
{
"@type": "Transition",
"@id": "urn:ujg:transition:spot-available",
"from": "urn:ujg:state:waitlisted",
"to": "urn:ujg:state:offer-available",
"label": "Spot becomes available"
},
{
"@type": "Transition",
"@id": "urn:ujg:transition:accept-offer",
"from": "urn:ujg:state:offer-available",
"to": "urn:ujg:state:confirmed",
"label": "Accept",
"conditionRef": "urn:ujg:condition:offer-valid",
"effectRef": "urn:ujg:effect:accept-offer"
},
{
"@type": "Transition",
"@id": "urn:ujg:transition:decline-offer",
"from": "urn:ujg:state:offer-available",
"to": "urn:ujg:state:waitlisted",
"label": "Decline"
},
{
"@type": "Transition",
"@id": "urn:ujg:transition:expire-offer",
"from": "urn:ujg:state:offer-available",
"to": "urn:ujg:state:offer-expired",
"label": "Expire"
},
{
"@type": "Condition",
"@id": "urn:ujg:condition:offer-valid"
},
{
"@type": "Effect",
"@id": "urn:ujg:effect:accept-offer"
},
{
"@type": "EntryBinding",
"@id": "urn:ujg:entry-binding:spot-offer",
"entryRef": "urn:ujg:entry:workshop-offer",
"value": "workshop-offer-entry"
},
{
"@type": "StateDomainRequirement",
"@id": "urn:ujg:domain-requirement:waitlisted",
"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",
"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",
"effectRef": "urn:ujg:effect:accept-offer",
"requirement": "Accepting the outstanding offer must consume that offer and establish confirmed workshop participation."
},
{
"@type": "EntryBindingDomainRequirement",
"@id": "urn:ujg:domain-requirement:offer-continuity",
"entryBindingRef": "urn:ujg:entry-binding:spot-offer",
"requirement": "The external application entry must resolve the same outstanding participant-specific offer represented by the notification."
}
]
} A downstream generator could satisfy these requirements using different implementation models. Those downstream domain concepts are intentionally not part of the UJG vocabulary.