Surface
draftStatus of this Document
This report was published by the User Journey 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 core-family specification describes how an intended journey is made visible to people. It defines four concepts:
A Persona describes a human role or perspective, such as a customer or support agent.
A Touchpoint describes where an experience is presented, such as a website, mobile app, kiosk, or call center.
A Surface gives a stable identity to something visible that represents a Graph node.
A SurfaceInstance identifies one concrete occurrence of a Surface at runtime.
The concepts form two independent relationship chains:
flowchart LR Persona -->|touchpointRefs| Touchpoint Touchpoint -->|journeyRefs| Journey SurfaceInstance -->|surfaceRef| Surface Surface -->|graphNodeRef| GraphNode["State, CompositeState, or Command"]
The first chain describes the human perspective and presenting boundary around a Journey. The second describes how visible materialization connects to Graph meaning. Neither chain adds Graph edges, changes traversal, or identifies who actually used a system at runtime.
Graph does not depend on Surface. A Graph model remains meaningful without personas, touchpoints, surfaces, or runtime instances.
Examples compose the shared baseline context with
https://ujg.specs.openuji.org/ed/ns/surface.context.jsonld.
2. Terminology
Persona: A human role, archetype, or participant perspective relevant to an experience.
Touchpoint: A system, channel, or service boundary through which one or more Journeys are presented.
Surface: A stable, addressable, design-system-agnostic visible boundary for one supported Graph node.
SurfaceInstance: One concrete runtime-visible occurrence of a Surface.
3. Persona
A Persona gives a name to a human perspective that matters to an experience. For example,
"Customer", "Support agent", and "Store manager" can be personas. A persona can be associated with
one or more Touchpoints through touchpointRefs.
A Persona is not a person record. It does not identify an account, authenticated identity, runtime actor, authorization subject, legal person, system, or organization. It also does not own Graph nodes. Graph nodes therefore never need a Persona in order to be understood.
A Persona MUST have an IRI so it can be referenced consistently.
A Persona MAY have a
label,tags, and one or moretouchpointRefs.Every
touchpointRefsvalue MUST identify a Touchpoint.Graph nodes MUST NOT reference a Persona directly.
Persona relationships MUST NOT create Graph edges, alter Graph traversal, assert that something occurred, identify a Runtime actor, or imply authentication or authorization.
classDiagram
class Persona {
id
label
tags
touchpointRefs
}
class Touchpoint
Persona --> "0..*" Touchpoint : touchpointRefs Example JSON nodes:
[
{
"@type": "Persona",
"@id": "urn:ujg:persona:customer",
"label": "Customer",
"tags": ["shopper"],
"touchpointRefs": ["urn:ujg:touchpoint:web"]
},
{
"@type": "Touchpoint",
"@id": "urn:ujg:touchpoint:web",
"label": "Web shop",
"channel": "web"
}
] [
{
"@type": "Persona",
"@id": "urn:ujg:persona:customer",
"label": "Customer",
"tags": ["shopper"],
"touchpointRefs": ["urn:ujg:touchpoint:web"]
},
{
"@type": "Touchpoint",
"@id": "urn:ujg:touchpoint:web",
"label": "Web shop",
"channel": "web"
}
] 4. Touchpoint
A Touchpoint describes where people encounter an experience. A website, mobile app, kiosk,
branch office, and call center are all possible touchpoints. The optional channel is a short name
for the kind of boundary, while journeyRefs identifies the Journeys presented there.
Touchpoints connect to meaningful Journey boundaries rather than individual States or Surfaces. This keeps a coherent part of an experience together. A Touchpoint does not identify a Persona, account, Runtime observer, or protocol state by itself.
A Touchpoint MUST have an IRI and one
label.A Touchpoint MAY have one
channeland one or morejourneyRefs.Every
journeyRefsvalue MUST identify a Journey.Touchpoint assignment SHOULD be made at a Journey boundary when that Journey represents one coherent experience on the Touchpoint.
journeyRefsMUST NOT create hidden Graph edges, change traversal, assert occurrence, or change Runtime event ordering.
classDiagram
class Touchpoint {
id
label
channel
journeyRefs
}
class Journey
Touchpoint --> "0..*" Journey : journeyRefs Example JSON node:
{
"@type": "Touchpoint",
"@id": "urn:ujg:touchpoint:web",
"label": "Web shop",
"channel": "web",
"journeyRefs": ["urn:ujg:journey:checkout"]
} {
"@type": "Touchpoint",
"@id": "urn:ujg:touchpoint:web",
"label": "Web shop",
"channel": "web",
"journeyRefs": ["urn:ujg:journey:checkout"]
} 5. Surface
A Surface gives a stable identity to one visible boundary and connects that boundary to its
Graph meaning with graphNodeRef. The referenced Graph node can be a State, CompositeState, or
Command.
For example, a shipping form can be the Surface for a shipping State. A visible "Place order"
control can be the Surface for a Command. Referencing a Command does not prescribe a button, link,
or any other widget; it only says which intentional invocation the visible boundary represents.
Multiple Surfaces can reference the same Graph node when they are genuinely different visible boundaries. A different color, framework, or renderer does not by itself make a new Surface.
When Graph traversal creates multiple concrete occurrences, the corresponding Surface can also appear more than once. Surface does not define occurrence counts, instance keys, collection data, or rendering behavior. Those details come from Graph semantics and runtime or application data.
A Surface MUST have an IRI and exactly one
graphNodeRef.graphNodeRefMUST identify aState,CompositeState, orCommand.A Surface MUST NOT declare a Persona or Touchpoint directly.
A Surface MUST NOT change Graph traversal or assert that its Graph node occurred.
A Surface MUST NOT define occurrence multiplicity, instance keys, data sources, collection iteration, or rendering behavior.
A Surface MUST NOT reference a Transition or OutgoingTransition through
graphNodeRef.
classDiagram
class State
class CompositeState
class Command
class Surface {
id
graphNodeRef
}
Surface --> State : graphNodeRef
Surface --> CompositeState : graphNodeRef
Surface --> Command : graphNodeRef Example JSON node:
{
"@type": "Surface",
"@id": "urn:ujg:surface:shipping-form",
"graphNodeRef": "urn:ujg:state:shipping"
} {
"@type": "Surface",
"@id": "urn:ujg:surface:shipping-form",
"graphNodeRef": "urn:ujg:state:shipping"
} 6. SurfaceInstance
A SurfaceInstance identifies one concrete occurrence of a Surface. For example, the same
shipping form Surface can appear during many executions, and each visible occurrence can have its
own SurfaceInstance. Runtime events use surfaceInstanceRef to say where an observed moment
occurred.
A SurfaceInstance MUST have an IRI and exactly one
surfaceRef.surfaceRefMUST identify a Surface.A SurfaceInstance MUST NOT declare Graph-node identity directly; its Graph meaning is found through the referenced Surface.
classDiagram
class Surface
class SurfaceInstance {
id
surfaceRef
}
SurfaceInstance --> Surface : surfaceRef Example JSON node:
{
"@type": "SurfaceInstance",
"@id": "urn:ujg:surface-instance:shipping-form:1",
"surfaceRef": "urn:ujg:surface:shipping-form"
} {
"@type": "SurfaceInstance",
"@id": "urn:ujg:surface-instance:shipping-form:1",
"surfaceRef": "urn:ujg:surface:shipping-form"
} 8. Migration From User
Documents written against an earlier Editor's Draft can migrate without changing their overall Graph structure:
Change each Surface-module
Usernode toPersona. Its existing@idmay stay the same.Remove
userReffrom every Graph node. Persona context is not inherited by entries, states, transitions, commands, exits, or child journeys.Connect each Persona to relevant Touchpoints with
touchpointRefs.Connect each Touchpoint to coherent Journey boundaries with
journeyRefs.
There is no compatibility alias for User or userRef in this Editor's Draft.
9. Normative Artifacts
The following sections contain the exact machine-readable vocabulary, JSON-LD term mappings, and validation rules. Readers who only need the conceptual model can continue to the examples.
9.1. Ontology
The Surface ontology is published at https://ujg.specs.openuji.org/ed/ns/surface.
@prefix ujg: <https://ujg.specs.openuji.org/ed/ns/core#> .
@prefix ujggraph: <https://ujg.specs.openuji.org/ed/ns/graph#> .
@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 xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix dct: <http://purl.org/dc/terms/> .
<https://ujg.specs.openuji.org/ed/ns/surface#> a owl:Ontology ;
rdfs:label "UJG Surface Editor's Draft Vocabulary"@en ;
dct:description "UJG Surface ontology declaration" .
### Classes
ujgsurface:Surface a owl:Class ;
rdfs:subClassOf ujg:Node .
ujgsurface:SurfaceInstance a owl:Class ;
rdfs:subClassOf ujg:Node .
ujgsurface:Touchpoint a owl:Class ;
rdfs:subClassOf ujg:Node .
ujgsurface:Persona a owl:Class ;
rdfs:subClassOf ujg:Node ;
rdfs:label "Persona" ;
rdfs:comment "A human role, archetype, or participant perspective associated with one or more human-facing touchpoints." .
### Properties
ujgsurface:graphNodeRef a owl:ObjectProperty ;
rdfs:domain ujgsurface:Surface ;
rdfs:range [
a owl:Class ;
owl:unionOf (
ujggraph:State
ujggraph:CompositeState
ujggraph:Command
)
] .
ujgsurface:surfaceRef a owl:ObjectProperty ;
rdfs:domain ujgsurface:SurfaceInstance ;
rdfs:range ujgsurface:Surface .
ujgsurface:touchpointRefs a owl:ObjectProperty ;
rdfs:domain ujgsurface:Persona ;
rdfs:range ujgsurface:Touchpoint ;
rdfs:label "touchpoint refs" ;
rdfs:comment "References touchpoints associated with this human role, archetype, or participant perspective." .
ujgsurface:journeyRefs a owl:ObjectProperty ;
rdfs:domain ujgsurface:Touchpoint ;
rdfs:range ujggraph:Journey ;
rdfs:label "journey refs" ;
rdfs:comment "References meaningful Journey boundaries presented through this Touchpoint." .
ujgsurface:channel a owl:DatatypeProperty ;
rdfs:domain ujgsurface:Touchpoint ;
rdfs:range xsd:string . @prefix ujg: <https://ujg.specs.openuji.org/ed/ns/core#> .
@prefix ujggraph: <https://ujg.specs.openuji.org/ed/ns/graph#> .
@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 xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix dct: <http://purl.org/dc/terms/> .
<https://ujg.specs.openuji.org/ed/ns/surface#> a owl:Ontology ;
rdfs:label "UJG Surface Editor's Draft Vocabulary"@en ;
dct:description "UJG Surface ontology declaration" .
### Classes
ujgsurface:Surface a owl:Class ;
rdfs:subClassOf ujg:Node .
ujgsurface:SurfaceInstance a owl:Class ;
rdfs:subClassOf ujg:Node .
ujgsurface:Touchpoint a owl:Class ;
rdfs:subClassOf ujg:Node .
ujgsurface:Persona a owl:Class ;
rdfs:subClassOf ujg:Node ;
rdfs:label "Persona" ;
rdfs:comment "A human role, archetype, or participant perspective associated with one or more human-facing touchpoints." .
### Properties
ujgsurface:graphNodeRef a owl:ObjectProperty ;
rdfs:domain ujgsurface:Surface ;
rdfs:range [
a owl:Class ;
owl:unionOf (
ujggraph:State
ujggraph:CompositeState
ujggraph:Command
)
] .
ujgsurface:surfaceRef a owl:ObjectProperty ;
rdfs:domain ujgsurface:SurfaceInstance ;
rdfs:range ujgsurface:Surface .
ujgsurface:touchpointRefs a owl:ObjectProperty ;
rdfs:domain ujgsurface:Persona ;
rdfs:range ujgsurface:Touchpoint ;
rdfs:label "touchpoint refs" ;
rdfs:comment "References touchpoints associated with this human role, archetype, or participant perspective." .
ujgsurface:journeyRefs a owl:ObjectProperty ;
rdfs:domain ujgsurface:Touchpoint ;
rdfs:range ujggraph:Journey ;
rdfs:label "journey refs" ;
rdfs:comment "References meaningful Journey boundaries presented through this Touchpoint." .
ujgsurface:channel a owl:DatatypeProperty ;
rdfs:domain ujgsurface:Touchpoint ;
rdfs:range xsd:string . 9.2. JSON-LD Context
The Surface context is published at https://ujg.specs.openuji.org/ed/ns/surface.context.jsonld.
{
"@context": {
"@version": 1.1,
"ujgsurface": "https://ujg.specs.openuji.org/ed/ns/surface#",
"surface": "https://ujg.specs.openuji.org/ed/ns/surface#",
"Surface": "ujgsurface:Surface",
"SurfaceInstance": "ujgsurface:SurfaceInstance",
"Touchpoint": "ujgsurface:Touchpoint",
"Persona": "ujgsurface:Persona",
"graphNodeRef": {
"@id": "ujgsurface:graphNodeRef",
"@type": "@id"
},
"surfaceRef": {
"@id": "ujgsurface:surfaceRef",
"@type": "@id"
},
"touchpointRefs": {
"@id": "ujgsurface:touchpointRefs",
"@type": "@id",
"@container": "@set"
},
"journeyRefs": {
"@id": "ujgsurface:journeyRefs",
"@type": "@id",
"@container": "@set"
},
"channel": "ujgsurface:channel"
}
} {
"@context": {
"@version": 1.1,
"ujgsurface": "https://ujg.specs.openuji.org/ed/ns/surface#",
"surface": "https://ujg.specs.openuji.org/ed/ns/surface#",
"Surface": "ujgsurface:Surface",
"SurfaceInstance": "ujgsurface:SurfaceInstance",
"Touchpoint": "ujgsurface:Touchpoint",
"Persona": "ujgsurface:Persona",
"graphNodeRef": {
"@id": "ujgsurface:graphNodeRef",
"@type": "@id"
},
"surfaceRef": {
"@id": "ujgsurface:surfaceRef",
"@type": "@id"
},
"touchpointRefs": {
"@id": "ujgsurface:touchpointRefs",
"@type": "@id",
"@container": "@set"
},
"journeyRefs": {
"@id": "ujgsurface:journeyRefs",
"@type": "@id",
"@container": "@set"
},
"channel": "ujgsurface:channel"
}
} 9.3. Validation
The Surface SHACL shape is published at https://ujg.specs.openuji.org/ed/ns/surface.shape.
@prefix ujggraph: <https://ujg.specs.openuji.org/ed/ns/graph#> .
@prefix ujgsurface: <https://ujg.specs.openuji.org/ed/ns/surface#> .
@prefix ujgsurfaceshape: <https://ujg.specs.openuji.org/ed/ns/surface.shape#> .
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
ujgsurfaceshape:GraphNodeRefTargetShape a sh:NodeShape ;
sh:nodeKind sh:IRI ;
sh:or (
[ sh:class ujggraph:State ]
[ sh:class ujggraph:CompositeState ]
[ sh:class ujggraph:Command ]
) .
ujgsurfaceshape:SurfaceShape a sh:NodeShape ;
sh:targetClass ujgsurface:Surface ;
sh:nodeKind sh:IRI ;
sh:property [
sh:path ujgsurface:graphNodeRef ;
sh:node ujgsurfaceshape:GraphNodeRefTargetShape ;
sh:nodeKind sh:IRI ;
sh:minCount 1 ;
sh:maxCount 1 ;
] .
ujgsurfaceshape:SurfaceInstanceShape a sh:NodeShape ;
sh:targetClass ujgsurface:SurfaceInstance ;
sh:nodeKind sh:IRI ;
sh:property [
sh:path ujgsurface:surfaceRef ;
sh:class ujgsurface:Surface ;
sh:nodeKind sh:IRI ;
sh:minCount 1 ;
sh:maxCount 1 ;
] .
ujgsurfaceshape:PersonaShape a sh:NodeShape ;
sh:targetClass ujgsurface:Persona ;
sh:nodeKind sh:IRI ;
sh:property [
sh:path ujggraph:label ;
sh:datatype xsd:string ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ujggraph:tags ;
sh:datatype xsd:string ;
] ;
sh:property [
sh:path ujgsurface:touchpointRefs ;
sh:class ujgsurface:Touchpoint ;
sh:nodeKind sh:IRI ;
] .
ujgsurfaceshape:TouchpointShape a sh:NodeShape ;
sh:targetClass ujgsurface:Touchpoint ;
sh:nodeKind sh:IRI ;
sh:property [
sh:path ujggraph:label ;
sh:datatype xsd:string ;
sh:minCount 1 ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ujgsurface:channel ;
sh:datatype xsd:string ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ujgsurface:journeyRefs ;
sh:class ujggraph:Journey ;
sh:nodeKind sh:IRI ;
] . @prefix ujggraph: <https://ujg.specs.openuji.org/ed/ns/graph#> .
@prefix ujgsurface: <https://ujg.specs.openuji.org/ed/ns/surface#> .
@prefix ujgsurfaceshape: <https://ujg.specs.openuji.org/ed/ns/surface.shape#> .
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
ujgsurfaceshape:GraphNodeRefTargetShape a sh:NodeShape ;
sh:nodeKind sh:IRI ;
sh:or (
[ sh:class ujggraph:State ]
[ sh:class ujggraph:CompositeState ]
[ sh:class ujggraph:Command ]
) .
ujgsurfaceshape:SurfaceShape a sh:NodeShape ;
sh:targetClass ujgsurface:Surface ;
sh:nodeKind sh:IRI ;
sh:property [
sh:path ujgsurface:graphNodeRef ;
sh:node ujgsurfaceshape:GraphNodeRefTargetShape ;
sh:nodeKind sh:IRI ;
sh:minCount 1 ;
sh:maxCount 1 ;
] .
ujgsurfaceshape:SurfaceInstanceShape a sh:NodeShape ;
sh:targetClass ujgsurface:SurfaceInstance ;
sh:nodeKind sh:IRI ;
sh:property [
sh:path ujgsurface:surfaceRef ;
sh:class ujgsurface:Surface ;
sh:nodeKind sh:IRI ;
sh:minCount 1 ;
sh:maxCount 1 ;
] .
ujgsurfaceshape:PersonaShape a sh:NodeShape ;
sh:targetClass ujgsurface:Persona ;
sh:nodeKind sh:IRI ;
sh:property [
sh:path ujggraph:label ;
sh:datatype xsd:string ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ujggraph:tags ;
sh:datatype xsd:string ;
] ;
sh:property [
sh:path ujgsurface:touchpointRefs ;
sh:class ujgsurface:Touchpoint ;
sh:nodeKind sh:IRI ;
] .
ujgsurfaceshape:TouchpointShape a sh:NodeShape ;
sh:targetClass ujgsurface:Touchpoint ;
sh:nodeKind sh:IRI ;
sh:property [
sh:path ujggraph:label ;
sh:datatype xsd:string ;
sh:minCount 1 ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ujgsurface:channel ;
sh:datatype xsd:string ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ujgsurface:journeyRefs ;
sh:class ujggraph:Journey ;
sh:nodeKind sh:IRI ;
] . 10. Examples
10.1. Combined Surface Example
In this example, the Customer Persona is associated with the Web shop Touchpoint. That Touchpoint presents the Checkout Journey. The shipping form Surface gives visible identity to the Shipping State, and the SurfaceInstance identifies one concrete occurrence of that form.
{
"@context": [
"https://ujg.specs.openuji.org/ed/ns/context.jsonld",
"https://ujg.specs.openuji.org/ed/ns/surface.context.jsonld"
],
"@id": "https://example.com/ujg/surface/checkout.jsonld",
"@type": "UJGDocument",
"nodes": [
{
"@type": "Persona",
"@id": "urn:ujg:persona:customer",
"label": "Customer",
"touchpointRefs": ["urn:ujg:touchpoint:web"]
},
{
"@type": "Touchpoint",
"@id": "urn:ujg:touchpoint:web",
"label": "Web shop",
"channel": "web",
"journeyRefs": ["urn:ujg:journey:checkout"]
},
{
"@type": "Journey",
"@id": "urn:ujg:journey:checkout",
"defaultEntryRef": "urn:ujg:entry:checkout-default",
"entryRefs": ["urn:ujg:entry:checkout-default"],
"stateRefs": ["urn:ujg:state:shipping"]
},
{
"@type": "JourneyEntry",
"@id": "urn:ujg:entry:checkout-default",
"stateRef": "urn:ujg:state:shipping"
},
{
"@type": "State",
"@id": "urn:ujg:state:shipping",
"label": "Shipping"
},
{
"@type": "Surface",
"@id": "urn:ujg:surface:shipping-form",
"graphNodeRef": "urn:ujg:state:shipping"
},
{
"@type": "SurfaceInstance",
"@id": "urn:ujg:surface-instance:shipping-form:1",
"surfaceRef": "urn:ujg:surface:shipping-form"
}
]
} {
"@context": [
"https://ujg.specs.openuji.org/ed/ns/context.jsonld",
"https://ujg.specs.openuji.org/ed/ns/surface.context.jsonld"
],
"@id": "https://example.com/ujg/surface/checkout.jsonld",
"@type": "UJGDocument",
"nodes": [
{
"@type": "Persona",
"@id": "urn:ujg:persona:customer",
"label": "Customer",
"touchpointRefs": ["urn:ujg:touchpoint:web"]
},
{
"@type": "Touchpoint",
"@id": "urn:ujg:touchpoint:web",
"label": "Web shop",
"channel": "web",
"journeyRefs": ["urn:ujg:journey:checkout"]
},
{
"@type": "Journey",
"@id": "urn:ujg:journey:checkout",
"defaultEntryRef": "urn:ujg:entry:checkout-default",
"entryRefs": ["urn:ujg:entry:checkout-default"],
"stateRefs": ["urn:ujg:state:shipping"]
},
{
"@type": "JourneyEntry",
"@id": "urn:ujg:entry:checkout-default",
"stateRef": "urn:ujg:state:shipping"
},
{
"@type": "State",
"@id": "urn:ujg:state:shipping",
"label": "Shipping"
},
{
"@type": "Surface",
"@id": "urn:ujg:surface:shipping-form",
"graphNodeRef": "urn:ujg:state:shipping"
},
{
"@type": "SurfaceInstance",
"@id": "urn:ujg:surface-instance:shipping-form:1",
"surfaceRef": "urn:ujg:surface:shipping-form"
}
]
} 10.2. Private Extension Payloads
Core extensions remains available for vendor-private Surface data.
{
"@id": "urn:ujg:surface:cart",
"@type": "Surface",
"graphNodeRef": "urn:ujg:state:cart",
"extensions": {
"com.acme.audit": { "reviewTicket": "ACME-1234" }
}
} {
"@id": "urn:ujg:surface:cart",
"@type": "Surface",
"graphNodeRef": "urn:ujg:state:cart",
"extensions": {
"com.acme.audit": { "reviewTicket": "ACME-1234" }
}
}