Skip to Content
DocumentationArchitecture

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, structure and rules layers

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/range of 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 the skos:*Match mappings.

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 objects

One 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 @context files 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 with skos: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:ConceptScheme of skos:Concepts; the scheme is the datatype. Hierarchies use skos:broader; cross-cutting groupings are advisory skos: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, a TransportMovement, a Party, a Product. It has a resolvable identity.
  • a value object is a bundle of attributes with no independent identity, inlined into its owner: a Measure, a Dimension, a MonetaryAmount. It aligns with schema:StructuredValue and 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

  1. This documentation site: a static Nextra  export published to GitLab Pages (home, architecture, the ontology pages, external vocabularies, governance).
  2. 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.

Last updated on