Architecture
The Vocab is a vocabulary, one layer in the broader architecture for exchanging supply-chain documents. That architecture works by keeping three concerns apart: the meaning of terms, the structure of a message, and the rules that apply in a given context. Separating them is what keeps each layer simple and usable on its own.
Meaning, structure and rules
Meaning: a vocabulary is a dictionary of terms. It says what Consignment, postalAddress or
party-role/consignee mean, and says nothing about how any particular message is shaped. This
decoupling is the whole point: the same term can be reused, unchanged, in an invoice, a product
passport, or a customs declaration.
Structure: a message (an invoice, a Digital Product Passport, a consignment) arranges terms into
a shape for a purpose. Structure is owned by the message designer, not the vocabulary. One message
may draw on several vocabularies to express its full intent: the Vocab for supply-chain terms,
schema.org for generic ones, QUDT for units,
GS1 for identifiers. The Vocab is one dictionary among several; a message points
its JSON-LD @context at whichever terms it needs.
Rules: the same structure is used in many contexts, each with its own constraints: which business-identifier scheme is mandatory in a jurisdiction, which fields a community requires, a validation profile (SHACL) for a particular exchange. Rules constrain a structure for a context without changing what the terms mean.
Keeping these apart is what keeps each layer simple: the vocabulary stays small, stable and reusable
across every message and jurisdiction; a message schema stays lean because it borrows meaning
instead of redefining it; and rules can vary by context without forking either the vocabulary or the
message. The Vocab occupies the meaning layer for the supply chain, the shared terms that UNTP and
UNVTD credentials, and other messages, re-point their @context at.
The rest of this page is about how that vocabulary is authored, built and deployed: one repository, one source of truth, many generated artefacts.
Built on 20+ years of Semantic Web standards
The Vocab is not a bespoke format. It is expressed in the mature, stable W3C Semantic Web stack that has underpinned linked data for over two decades:
- RDF : the universal data model; everything is a triple (subject → predicate → object).
- RDFS : classes, properties, labels and the
domain/rangeof relationships. - OWL : richer semantics where needed (equivalence, disjointness, characteristics of properties).
- SKOS : concept schemes for the code lists:
skos:Concept,skos:broader,skos:Collection, and theskos:*Matchmappings.
Resting on these standards means the vocabulary is interoperable with every RDF tool, triple store, reasoner and validator in the ecosystem. Nothing here is invented, and nothing is locked to one vendor.
Source of truth: Turtle
Those standards have several serialisations; the normative one here is
Turtle (TTL), the most human-readable RDF syntax, chosen
because it is compact and diff-reviewable (the whole point of moving governance onto merge
requests). The vocabulary is authored directly as Turtle under ontology/, one file per domain plus
core:
ontology/
<domain>/
<domain>.ttl the vocabulary (classes, properties)
code-lists/ SKOS code lists for the domain
docs/ generated diagram + tables
core/ … cross-cutting: Event, TradeDocument, value objectsOne source, many published formats
Because the source is standards-based RDF, publishing is just a programmatic transformation of the same graph into whatever shape a consumer needs, never a separate hand-maintained copy that could drift:
- JSON-LD: for credential
@contextfiles and web developers; - RDF/XML & N-Triples: for classic RDF toolchains;
- HTML: the human-browsable term and code pages (content-negotiated);
- SHACL: validation shapes;
- CSV: flat code-list extracts for spreadsheets and data teams;
- diagrams: the domain models on these pages.
Every one of these is generated from the Turtle in CI. The TTL moves; the formats follow automatically. This is the payoff of a standards-based normative format: author once, publish anywhere.
Naming conventions
- Flat term URIs:
https://vocabulary.uncefact.org/<Term>. No domain and no version in the path; the domain is metadata (rdfs:isDefinedBy), not a URI segment, so a term can be re-homed without a URI change. - Path-style code URIs: a code value is
https://vocabulary.uncefact.org/<scheme>/<code>(e.g.…/party-role/consignee). Flat leaf identity, hierarchy expressed withskos:broader(never a parent in the path), so a code survives reclassification. - Stable forever: a published URI never changes meaning. Corrections are new terms; retired
terms keep resolving (
owl:deprecated), so historic documents stay verifiable.
Structural model
- Semantics, not structure: the vocabulary carries meaning (classes, properties, code lists); documents/credentials carry composition. Structural join nodes (party-role, material-usage…) and code-carrier tuples (country, classification) are untyped n-ary relations, not classes.
- Code lists are SKOS: every code list is a
skos:ConceptSchemeofskos:Concepts; the scheme is the datatype. Hierarchies useskos:broader; cross-cutting groupings are advisoryskos:Collections. - Coexist and map: the Vocab keeps its own foundational terms even where schema.org/GS1/QUDT overlap, and reconciles by mapping, never deletion. See the external-vocabulary register.
Identity and identifiers
Every class is one of two kinds, declared explicitly (unece:objectNature) so the distinction is
never left to guesswork:
- an entity is an identified thing that exists in its own right and is referenced across
documents and time: a
Consignment, aTransportMovement, aParty, aProduct. It has a resolvable identity. - a value object is a bundle of attributes with no independent identity, inlined into its owner:
a
Measure, aDimension, aMonetaryAmount. It aligns withschema:StructuredValueand carries no identifier.
An entity’s own identity is its node URI (the JSON-LD @id), so nothing extra is needed to
identify it. Real-world entities usually also carry one or more scheme-issued identifiers: a
product has a GTIN, an organisation an LEI or a DUNS number, a facility a GLN. These are modelled
with the Identifier value object, following schema.org’s
PropertyValue pattern: a registeredId (the value) plus an
idScheme (the scheme that issued it), with an optional name.
"identifier": [
{ "registeredId": "09520123456788", "idScheme": { "id": "https://www.gs1.org/gtin", "name": "GS1 GTIN" } },
{ "registeredId": "ACME-SKU-42", "idScheme": { "id": "https://acme.example/skus", "name": "ACME SKU" } }
]The identifier property is the same on every entity, and an entity may hold as many as it needs.
How many is a structure-and-rules question (an array in a message schema, a mandatory scheme in a
jurisdiction), so it is deliberately left out of the vocabulary. This replaces two patterns that do
not scale: a separate property per scheme (gtinId, dunsId, glnId…), and a single flat
scheme+value pair that cannot express more than one identifier.
Two deployments from one repo
- This documentation site: a static Nextra export published to GitLab Pages (home, architecture, the ontology pages, external vocabularies, governance).
- The resolvable vocabulary: the term and code URIs under
vocabulary.uncefact.org, content-negotiated to TTL / JSON-LD / HTML (the normative artefacts generated from the same TTL source).
CI gates
Every push and merge request runs scripts/validate-vocab.py, which performs eight checks: Turtle
syntax, referential integrity, file↔domain consistency, the term gate (definition + termStatus),
flat-URI uniqueness, the code-list profile (scheme metadata, language-tagged labels, notation,
hierarchy integrity), code-list-ledger drift, and object nature (every class declared entity or
value object). Fidelity to the UNTP source is separately checked by scripts/fidelity-check.py. The
site deploys only after validation passes.