Data Contract
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
This optional module defines a minimal vocabulary for structured data associated with visible UJG surfaces.
A data contract gives stable UJG identity to an external JSON Schema document and binds that schema
to one Surface. The binding does not change Graph topology, Surface attachment, Runtime event
shape, Domain Model meaning, or implementation architecture. Consumers derive the contract's role
from the referenced surface's existing graphNodeRef.
2. Terminology
DataSchema: An addressable reference to an external JSON Schema document.
DataBinding: An addressable association between one Surface and one DataSchema.
Surface data contract: Structured data made available to materialize one concrete occurrence of a state or composite-state surface.
Command invocation data contract: Structured data explicitly supplied when invoking a visible command surface.
3. DataSchema
A DataSchema identifies an external JSON Schema document. The JSON Schema document describes a structured data contract, but its contents are not embedded into the UJG RDF node.
A DataSchema MUST be identified by an IRI.
A DataSchema MAY declare at most one
label.A DataSchema MUST declare exactly one
dataSchemaSource.dataSchemaSourceMUST identify an external JSON Schema document.JSON Schema Draft 2020-12 MUST be used for this version.
The JSON Schema document MUST NOT be embedded into the DataSchema node.
Relative
dataSchemaSourceIRIs MUST be resolved against the containing UJGDocument's base IRI.
classDiagram
class DataSchema {
id
label
dataSchemaSource
} Example JSON node:
{
"@type": "DataSchema",
"@id": "urn:ujg:data-schema:workshop-detail",
"label": "Workshop detail data",
"dataSchemaSource": "./schemas/workshop-detail.schema.json"
} {
"@type": "DataSchema",
"@id": "urn:ujg:data-schema:workshop-detail",
"label": "Workshop detail data",
"dataSchemaSource": "./schemas/workshop-detail.schema.json"
} 4. DataBinding
A DataBinding associates one structured-data schema with one Surface. The referenced surface determines whether the schema describes data made available for visible materialization or data explicitly supplied through a visible invocation.
A DataBinding MUST be identified by an IRI.
A DataBinding MAY declare at most one
label.A DataBinding MUST declare exactly one
dataSurfaceRef.Every
dataSurfaceRefvalue MUST reference a Surface.A DataBinding MUST declare exactly one
dataSchemaRef.Every
dataSchemaRefvalue MUST reference a DataSchema.A Surface MUST be referenced by at most one DataBinding.
Multiple Surfaces MAY reuse the same DataSchema.
classDiagram
class Surface
class DataSchema
class DataBinding {
id
label
dataSurfaceRef
dataSchemaRef
}
DataBinding --> Surface : dataSurfaceRef
DataBinding --> DataSchema : dataSchemaRef Example JSON node:
{
"@type": "DataBinding",
"@id": "urn:ujg:data-binding:workshop-detail",
"dataSurfaceRef": "urn:ujg:surface:workshop-registration-open",
"dataSchemaRef": "urn:ujg:data-schema:workshop-detail"
} {
"@type": "DataBinding",
"@id": "urn:ujg:data-binding:workshop-detail",
"dataSurfaceRef": "urn:ujg:surface:workshop-registration-open",
"dataSchemaRef": "urn:ujg:data-schema:workshop-detail"
} 5. Attachment Model
The module attaches data contracts only to Surface nodes.
Consumers MUST derive the semantic role of a DataBinding by following:
DataBinding.dataSurfaceRef
-> Surface.graphNodeRef
-> State | CompositeState | Command DataBinding.dataSurfaceRef
-> Surface.graphNodeRef
-> State | CompositeState | Command DataBinding MUST NOT duplicate graphNodeRef.
6. State And CompositeState Surface Contracts
When a DataBinding references a Surface whose graphNodeRef points to a State or
CompositeState, the referenced DataSchema describes the structured application data made
available to materialize one concrete occurrence of that surface.
The schema may describe values such as a workshop title, date, location, or registration status. It does not define where those values come from, how they are queried, whether they are cached, or how they map to domain properties.
For a State with multiInstance: true, the DataBinding describes one concrete surface
occurrence. This module does not define collection sources, queries, iteration, pagination,
occurrence counts, entity enumeration, loading behavior, or rendering behavior.
7. Command Surface Contracts
When a DataBinding references a Surface whose graphNodeRef points to a Command, the
referenced DataSchema describes structured data explicitly supplied when the visible invocation
represented by that surface is invoked.
Do not combine surface materialization data and command invocation data merely because both participate in one form experience. The form's state surface can have one data contract for materialization, and the submit command's surface can have another data contract for explicitly supplied invocation data.
A Command may be referenced by several Transitions. There is still only one invocation data contract for that command's visible surface. Conditions determine the resulting branch; they do not define separate submission payloads.
A visible Command may require no explicitly supplied structured data. In that case, its DataSchema MAY reference an empty JSON object schema. Do not force ambient context such as route parameters, current identity, selected entity, session values, or implementation state into a command data contract.
8. Result Data
This module does not define command-response or transition-result schemas. Resulting visible data is contracted at the resulting Surface:
Command
-> Transition / Condition / Effect
-> next State
-> Surface
-> DataBinding Command
-> Transition / Condition / Effect
-> next State
-> Surface
-> DataBinding Transport response envelopes, protocol payloads, and implementation result objects remain outside this module.
9. Relationship To Other Modules
Data Contract and Domain Model have different responsibilities. Domain Model describes technology-neutral domain meaning. Data Contract describes structured data exposed at a user-facing materialization boundary or explicit invocation boundary.
Do not require Data Contract to depend on Domain Model. Do not automatically turn data-schema fields into Domain Model properties, and do not require Domain Model properties to appear in data schemas. A downstream realization MAY project domain state into surface data, but that mapping is outside this module.
Entry Binding identifies how an external invocation enters a Journey. Data Contract describes structured application data associated with a visible Surface. Neither replaces the other.
10. Non-Goals
Data Contract does not define:
REST, GraphQL, RPC, HTTP bodies, endpoints, URL parameters, or transport envelopes
server/client ownership, persistence, database schemas, framework state, React props, loaders, or queries
caching, authentication, authorization, sessions, or identity resolution
schema generation, data fetching, collection iteration, pagination, filtering, or sorting
mapping between JSON Schema fields and Domain Model properties
Runtime observed payload shape or Mapping interpretation rules
command-response or transition-result payloads
11. Normative Artifacts
This module is published through the following artifacts:
data-contract.ttl: ontology, published athttps://ujg.specs.openuji.org/ed/ns/data-contractdata-contract.context.jsonld: JSON-LD term mappings, published athttps://ujg.specs.openuji.org/ed/ns/data-contract.context.jsonlddata-contract.shape.ttl: SHACL validation rules, published athttps://ujg.specs.openuji.org/ed/ns/data-contract.shape
Examples in this page compose the Core, Graph, Surface, and Data Contract contexts.
11.1. Ontology
The normative Data Contract ontology is defined below and is published at
https://ujg.specs.openuji.org/ed/ns/data-contract.
@prefix ujg: <https://ujg.specs.openuji.org/ed/ns/core#> .
@prefix ujgdata: <https://ujg.specs.openuji.org/ed/ns/data-contract#> .
@prefix ujgsurface: <https://ujg.specs.openuji.org/ed/ns/surface#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix dct: <http://purl.org/dc/terms/> .
<https://ujg.specs.openuji.org/ed/ns/data-contract#> a owl:Ontology ;
rdfs:label "UJG Data Contract Editor's Draft Vocabulary"@en ;
dct:description "UJG Data Contract ontology declaration" .
### Classes
ujgdata:DataSchema a owl:Class ;
rdfs:subClassOf ujg:Node .
ujgdata:DataBinding a owl:Class ;
rdfs:subClassOf ujg:Node .
### Properties
ujgdata:dataSchemaSource a owl:ObjectProperty ;
rdfs:domain ujgdata:DataSchema ;
rdfs:range rdfs:Resource ;
rdfs:label "data schema source" ;
rdfs:comment "References the external JSON Schema document defining this data schema." .
ujgdata:dataSurfaceRef a owl:ObjectProperty ;
rdfs:domain ujgdata:DataBinding ;
rdfs:range ujgsurface:Surface ;
rdfs:label "data surface ref" ;
rdfs:comment "References the Surface whose structured data contract is described." .
ujgdata:dataSchemaRef a owl:ObjectProperty ;
rdfs:domain ujgdata:DataBinding ;
rdfs:range ujgdata:DataSchema ;
rdfs:label "data schema ref" ;
rdfs:comment "References the DataSchema associated with the bound Surface." . @prefix ujg: <https://ujg.specs.openuji.org/ed/ns/core#> .
@prefix ujgdata: <https://ujg.specs.openuji.org/ed/ns/data-contract#> .
@prefix ujgsurface: <https://ujg.specs.openuji.org/ed/ns/surface#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix dct: <http://purl.org/dc/terms/> .
<https://ujg.specs.openuji.org/ed/ns/data-contract#> a owl:Ontology ;
rdfs:label "UJG Data Contract Editor's Draft Vocabulary"@en ;
dct:description "UJG Data Contract ontology declaration" .
### Classes
ujgdata:DataSchema a owl:Class ;
rdfs:subClassOf ujg:Node .
ujgdata:DataBinding a owl:Class ;
rdfs:subClassOf ujg:Node .
### Properties
ujgdata:dataSchemaSource a owl:ObjectProperty ;
rdfs:domain ujgdata:DataSchema ;
rdfs:range rdfs:Resource ;
rdfs:label "data schema source" ;
rdfs:comment "References the external JSON Schema document defining this data schema." .
ujgdata:dataSurfaceRef a owl:ObjectProperty ;
rdfs:domain ujgdata:DataBinding ;
rdfs:range ujgsurface:Surface ;
rdfs:label "data surface ref" ;
rdfs:comment "References the Surface whose structured data contract is described." .
ujgdata:dataSchemaRef a owl:ObjectProperty ;
rdfs:domain ujgdata:DataBinding ;
rdfs:range ujgdata:DataSchema ;
rdfs:label "data schema ref" ;
rdfs:comment "References the DataSchema associated with the bound Surface." . 11.2. JSON-LD Context
The normative Data Contract JSON-LD context is defined below and is published at
https://ujg.specs.openuji.org/ed/ns/data-contract.context.jsonld.
{
"@context": {
"@version": 1.1,
"ujgdata": "https://ujg.specs.openuji.org/ed/ns/data-contract#",
"dataContract": "https://ujg.specs.openuji.org/ed/ns/data-contract#",
"DataSchema": "ujgdata:DataSchema",
"DataBinding": "ujgdata:DataBinding",
"dataSchemaSource": {
"@id": "ujgdata:dataSchemaSource",
"@type": "@id"
},
"dataSurfaceRef": {
"@id": "ujgdata:dataSurfaceRef",
"@type": "@id"
},
"dataSchemaRef": {
"@id": "ujgdata:dataSchemaRef",
"@type": "@id"
}
}
} {
"@context": {
"@version": 1.1,
"ujgdata": "https://ujg.specs.openuji.org/ed/ns/data-contract#",
"dataContract": "https://ujg.specs.openuji.org/ed/ns/data-contract#",
"DataSchema": "ujgdata:DataSchema",
"DataBinding": "ujgdata:DataBinding",
"dataSchemaSource": {
"@id": "ujgdata:dataSchemaSource",
"@type": "@id"
},
"dataSurfaceRef": {
"@id": "ujgdata:dataSurfaceRef",
"@type": "@id"
},
"dataSchemaRef": {
"@id": "ujgdata:dataSchemaRef",
"@type": "@id"
}
}
} 11.3. Validation
The normative Data Contract SHACL shape is defined below and is published at
https://ujg.specs.openuji.org/ed/ns/data-contract.shape.
@prefix ujgdata: <https://ujg.specs.openuji.org/ed/ns/data-contract#> .
@prefix ujgdatashape: <https://ujg.specs.openuji.org/ed/ns/data-contract.shape#> .
@prefix ujggraph: <https://ujg.specs.openuji.org/ed/ns/graph#> .
@prefix ujgsurface: <https://ujg.specs.openuji.org/ed/ns/surface#> .
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
ujgdatashape:DataSchemaShape a sh:NodeShape ;
sh:targetClass ujgdata:DataSchema ;
sh:nodeKind sh:IRI ;
sh:property [
sh:path ujggraph:label ;
sh:datatype xsd:string ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ujgdata:dataSchemaSource ;
sh:nodeKind sh:IRI ;
sh:minCount 1 ;
sh:maxCount 1 ;
] .
ujgdatashape:DataBindingShape a sh:NodeShape ;
sh:targetClass ujgdata:DataBinding ;
sh:nodeKind sh:IRI ;
sh:property [
sh:path ujggraph:label ;
sh:datatype xsd:string ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ujgdata:dataSurfaceRef ;
sh:class ujgsurface:Surface ;
sh:nodeKind sh:IRI ;
sh:minCount 1 ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ujgdata:dataSchemaRef ;
sh:class ujgdata:DataSchema ;
sh:nodeKind sh:IRI ;
sh:minCount 1 ;
sh:maxCount 1 ;
] .
ujgdatashape:DataSchemaSourceUsageShape a sh:NodeShape ;
sh:targetSubjectsOf ujgdata:dataSchemaSource ;
sh:class ujgdata:DataSchema ;
sh:nodeKind sh:IRI .
ujgdatashape:DataBindingUsageShape a sh:NodeShape ;
sh:targetSubjectsOf ujgdata:dataSurfaceRef ;
sh:targetSubjectsOf ujgdata:dataSchemaRef ;
sh:class ujgdata:DataBinding ;
sh:nodeKind sh:IRI .
ujgdatashape:SurfaceDataBindingCardinalityShape a sh:NodeShape ;
sh:targetClass ujgsurface:Surface ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A Surface must be referenced by at most one DataBinding through dataSurfaceRef." ;
sh:select """
PREFIX ujgdata: <https://ujg.specs.openuji.org/ed/ns/data-contract#>
SELECT $this WHERE {
?first ujgdata:dataSurfaceRef $this .
?second ujgdata:dataSurfaceRef $this .
FILTER(str(?first) < str(?second))
}
""" ;
] . @prefix ujgdata: <https://ujg.specs.openuji.org/ed/ns/data-contract#> .
@prefix ujgdatashape: <https://ujg.specs.openuji.org/ed/ns/data-contract.shape#> .
@prefix ujggraph: <https://ujg.specs.openuji.org/ed/ns/graph#> .
@prefix ujgsurface: <https://ujg.specs.openuji.org/ed/ns/surface#> .
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
ujgdatashape:DataSchemaShape a sh:NodeShape ;
sh:targetClass ujgdata:DataSchema ;
sh:nodeKind sh:IRI ;
sh:property [
sh:path ujggraph:label ;
sh:datatype xsd:string ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ujgdata:dataSchemaSource ;
sh:nodeKind sh:IRI ;
sh:minCount 1 ;
sh:maxCount 1 ;
] .
ujgdatashape:DataBindingShape a sh:NodeShape ;
sh:targetClass ujgdata:DataBinding ;
sh:nodeKind sh:IRI ;
sh:property [
sh:path ujggraph:label ;
sh:datatype xsd:string ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ujgdata:dataSurfaceRef ;
sh:class ujgsurface:Surface ;
sh:nodeKind sh:IRI ;
sh:minCount 1 ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ujgdata:dataSchemaRef ;
sh:class ujgdata:DataSchema ;
sh:nodeKind sh:IRI ;
sh:minCount 1 ;
sh:maxCount 1 ;
] .
ujgdatashape:DataSchemaSourceUsageShape a sh:NodeShape ;
sh:targetSubjectsOf ujgdata:dataSchemaSource ;
sh:class ujgdata:DataSchema ;
sh:nodeKind sh:IRI .
ujgdatashape:DataBindingUsageShape a sh:NodeShape ;
sh:targetSubjectsOf ujgdata:dataSurfaceRef ;
sh:targetSubjectsOf ujgdata:dataSchemaRef ;
sh:class ujgdata:DataBinding ;
sh:nodeKind sh:IRI .
ujgdatashape:SurfaceDataBindingCardinalityShape a sh:NodeShape ;
sh:targetClass ujgsurface:Surface ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A Surface must be referenced by at most one DataBinding through dataSurfaceRef." ;
sh:select """
PREFIX ujgdata: <https://ujg.specs.openuji.org/ed/ns/data-contract#>
SELECT $this WHERE {
?first ujgdata:dataSurfaceRef $this .
?second ujgdata:dataSurfaceRef $this .
FILTER(str(?first) < str(?second))
}
""" ;
] . The remaining module semantics beyond the structural SHACL constraints are:
Surface attachment only: Data bindings target Surface nodes through
dataSurfaceRefand MUST NOT attach schemas directly to Graph, Condition, or Effect nodes.Context-derived role: Consumers MUST derive materialization or invocation role through
DataBinding.dataSurfaceRef -> Surface.graphNodeRef.External schemas: A DataSchema MUST reference one external JSON Schema Draft 2020-12 document through
dataSchemaSource; the schema itself remains outside the RDF node.One binding per surface: A Surface MUST be referenced by at most one DataBinding, but multiple surfaces MAY reuse the same schema.
Realization neutrality: Data Contract MUST NOT define transport, persistence, query, framework, runtime payload, or command-result semantics.
Graceful degradation: A consumer that does not implement this module MAY ignore Data Contract semantics, but it SHOULD preserve recognized JSON-LD data during read-transform-write when possible.
12. Examples
12.1. State Surface Data
{
"@context": [
"https://ujg.specs.openuji.org/ed/ns/core.context.jsonld",
"https://ujg.specs.openuji.org/ed/ns/graph.context.jsonld",
"https://ujg.specs.openuji.org/ed/ns/surface.context.jsonld",
"https://ujg.specs.openuji.org/ed/ns/data-contract.context.jsonld"
],
"@id": "https://example.com/ujg/workshops/detail.jsonld",
"@type": "UJGDocument",
"nodes": [
{
"@type": "State",
"@id": "urn:ujg:state:workshop-detail",
"label": "Workshop detail"
},
{
"@type": "Surface",
"@id": "urn:ujg:surface:workshop-detail",
"graphNodeRef": "urn:ujg:state:workshop-detail"
},
{
"@type": "DataSchema",
"@id": "urn:ujg:data-schema:workshop-detail",
"label": "Workshop detail data",
"dataSchemaSource": "./schemas/workshop-detail.schema.json"
},
{
"@type": "DataBinding",
"@id": "urn:ujg:data-binding:workshop-detail",
"dataSurfaceRef": "urn:ujg:surface:workshop-detail",
"dataSchemaRef": "urn:ujg:data-schema:workshop-detail"
}
]
} {
"@context": [
"https://ujg.specs.openuji.org/ed/ns/core.context.jsonld",
"https://ujg.specs.openuji.org/ed/ns/graph.context.jsonld",
"https://ujg.specs.openuji.org/ed/ns/surface.context.jsonld",
"https://ujg.specs.openuji.org/ed/ns/data-contract.context.jsonld"
],
"@id": "https://example.com/ujg/workshops/detail.jsonld",
"@type": "UJGDocument",
"nodes": [
{
"@type": "State",
"@id": "urn:ujg:state:workshop-detail",
"label": "Workshop detail"
},
{
"@type": "Surface",
"@id": "urn:ujg:surface:workshop-detail",
"graphNodeRef": "urn:ujg:state:workshop-detail"
},
{
"@type": "DataSchema",
"@id": "urn:ujg:data-schema:workshop-detail",
"label": "Workshop detail data",
"dataSchemaSource": "./schemas/workshop-detail.schema.json"
},
{
"@type": "DataBinding",
"@id": "urn:ujg:data-binding:workshop-detail",
"dataSurfaceRef": "urn:ujg:surface:workshop-detail",
"dataSchemaRef": "urn:ujg:data-schema:workshop-detail"
}
]
} 12.2. Command Invocation Data
{
"@context": [
"https://ujg.specs.openuji.org/ed/ns/core.context.jsonld",
"https://ujg.specs.openuji.org/ed/ns/graph.context.jsonld",
"https://ujg.specs.openuji.org/ed/ns/surface.context.jsonld",
"https://ujg.specs.openuji.org/ed/ns/data-contract.context.jsonld"
],
"@id": "https://example.com/ujg/workshops/register.jsonld",
"@type": "UJGDocument",
"nodes": [
{
"@type": "Command",
"@id": "urn:ujg:command:submit-registration",
"label": "Submit registration"
},
{
"@type": "Surface",
"@id": "urn:ujg:surface:submit-registration",
"graphNodeRef": "urn:ujg:command:submit-registration"
},
{
"@type": "DataSchema",
"@id": "urn:ujg:data-schema:registration-submission",
"label": "Registration submission",
"dataSchemaSource": "./schemas/registration-submission.schema.json"
},
{
"@type": "DataBinding",
"@id": "urn:ujg:data-binding:submit-registration",
"dataSurfaceRef": "urn:ujg:surface:submit-registration",
"dataSchemaRef": "urn:ujg:data-schema:registration-submission"
}
]
} {
"@context": [
"https://ujg.specs.openuji.org/ed/ns/core.context.jsonld",
"https://ujg.specs.openuji.org/ed/ns/graph.context.jsonld",
"https://ujg.specs.openuji.org/ed/ns/surface.context.jsonld",
"https://ujg.specs.openuji.org/ed/ns/data-contract.context.jsonld"
],
"@id": "https://example.com/ujg/workshops/register.jsonld",
"@type": "UJGDocument",
"nodes": [
{
"@type": "Command",
"@id": "urn:ujg:command:submit-registration",
"label": "Submit registration"
},
{
"@type": "Surface",
"@id": "urn:ujg:surface:submit-registration",
"graphNodeRef": "urn:ujg:command:submit-registration"
},
{
"@type": "DataSchema",
"@id": "urn:ujg:data-schema:registration-submission",
"label": "Registration submission",
"dataSchemaSource": "./schemas/registration-submission.schema.json"
},
{
"@type": "DataBinding",
"@id": "urn:ujg:data-binding:submit-registration",
"dataSurfaceRef": "urn:ujg:surface:submit-registration",
"dataSchemaRef": "urn:ujg:data-schema:registration-submission"
}
]
} 12.3. External JSON Schema
The referenced JSON Schema document is external to UJG. This example is illustrative only; its fields are not Data Contract vocabulary.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "urn:ujg:data-schema:workshop-detail",
"type": "object",
"required": ["workshop"],
"properties": {
"workshop": {
"type": "object",
"required": ["id", "title", "startsAt"],
"properties": {
"id": { "type": "string" },
"title": { "type": "string" },
"startsAt": { "type": "string", "format": "date-time" }
},
"additionalProperties": false
}
},
"additionalProperties": false
} {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "urn:ujg:data-schema:workshop-detail",
"type": "object",
"required": ["workshop"],
"properties": {
"workshop": {
"type": "object",
"required": ["id", "title", "startsAt"],
"properties": {
"id": { "type": "string" },
"title": { "type": "string" },
"startsAt": { "type": "string", "format": "date-time" }
},
"additionalProperties": false
}
},
"additionalProperties": false
}