Ontology engineering · a working brief

Model the domain, not the message.

Why you should never derive an ontology module from the JSON or XML that carries messages between systems — and what to build instead.

Written for Data analysts Integration engineers modelling a domain

The argument, up front

Thesis

A transport message tells you how data is shipped. An ontology says what is true in the world. Copy the first into the second and you freeze the accidents of a wire format into your axioms — and lose the very thing the ontology was for.

It is a tempting shortcut: the payload is concrete, it is right there, and it is “the data we actually have.” But a message is one system’s projection of one interaction, shaped by a protocol. Its structure — envelopes, arrays, embedded copies, string-typed codes — exists to serialize, not to describe. Reverse-engineer classes and properties from it and every one of those accidents becomes a modelling commitment you did not mean to make.

This brief is the companion to Start with the questions. That one names what should drive a module — the questions the business must answer. This one names what must not: the shape of the messages that happen to move the data around.

01 — THE MISTAKE

A category error, not a shortcut

The message and the domain are different kinds of thing, and conflating them is a category error — the same family of mistake a well-built ontology guards against (has-a, not is-a, for terms; no serialization-shaped intersections). Read a payload field by field and you will find that most of what is there is about the transmission, not about insurance.

What the payload carries vs. what the domain means

In the message (how it’s shipped)In the ontology (what is true)Why they differ
Envelope, batch / items[] wrapper— nothing —A container for transport, not a thing in the world
Array order, positional identityA set of individuals; order is not identitySequence is a serialization detail
Party embedded inline, repeated per branchOne Party, one IRI, shared everywhereTrees duplicate; graphs unify
"amount": 500.00core:MoneyAmount (value + currency)A concept, not a bare number
"status": "2" (code string)A Classifier drawn from a schemeCoded values name concepts, not literals
Required / optional fieldOpen-world; cardinality is a claim about realityAbsence in a message ≠ absence in the world
Correlation id, timestamp, retry count— nothing —About the communication act, not the domain

Two whole rows map to nothing: envelopes and correlation metadata belong to the message, and importing them pollutes an ins/ module with plumbing. The rows that do map, map differently — because the domain has identity, typed concepts, and open-world semantics that a flat payload throws away.

02 — THE FAILURE MODES

Six ways the format corrupts the model

Beyond the field-by-field leaks, letting the message’s shape drive the model does structural damage that is hard to undo later.

identity

It fragments identity

Messages are trees; domains are graphs. The same entity is inlined in one branch and referenced in another, or copied three times. The ontology needs one stable IRI per thing — message-shaped models scatter it across duplicated blobs with no inverses.

semantics

It smuggles in closed-world thinking

JSON Schema and XSD are closed-world validation contracts; OWL 2 DL is open-world and inference-driven. Model from the payload and you treat “absent” as “false” and “required” as cardinality — semantically wrong under OWL, and it defeats the reasoner.

coupling

It chains a stable layer to a volatile one

Wire formats change for operational reasons — a new API version, a renamed field, added batching — far more often than the domain concept does. Mirror the message and every wire tweak forces an ontology change, destroying the stability that is the point.

viewpoint

It bakes in one projection

A “quote request” payload encodes one system’s use case and its omissions. The ontology should be the integration model that many messages reconcile against — not a copy of whichever one you started from.

concepts

It flattens concepts to primitives

Wire formats reduce everything to strings, numbers and enums. A premium becomes a bare decimal instead of a core:MoneyAmount; a code becomes a string instead of a classifier. The conceptual structure the ontology exists to hold is gone.

reasoning

It throws away the payoff

Message-shaped models are inference-poor DTO graphs: no meaningful subclasses, disjointness, roles or inverses. You lose the role-reification pattern and mereology — and the competency questions become unanswerable.

The same policy fact, modelled two ways

Shaped by the message

  • Envelope classes. ClaimBatch and hasItem describe the document, not the world.
  • Duplicated party. Insured details embedded in every claim, with no shared identity.
  • Flattened money. premium is an xsd:decimal; the currency is lost.
  • Transmission cruft. Correlation ids and timestamps sitting in the domain model.

Shaped by the domain

  • Real entities only. claim:Claim links to policy:Policy via policy:underPolicy.
  • Shared identity. One Party IRI, referenced by every claim and policy that involves it.
  • Typed concepts. coverage:Premium ⊑ core:MoneyAmount — value and currency together.
  • Clean boundary. Message metadata stays in the payload, mapped in at the edge.
03 — THE CORE OF IT

A tree is not a graph

If one idea explains all the rest, it is this. A message is a tree: hierarchical containment, chosen for self-contained transport. A domain is a graph: shared entities identified once and pointed at from many places. Derive structure from the tree and you fragment exactly the identity the graph exists to unify.

Message · a tree

partyparty

Domain · a graph

:party-771
Left: the message repeats the same real party in two branches — local, positional, unshared. Right: the domain has one party, a stable IRI that many entities point at. Deriving the model from the tree manufactures the duplication the graph is meant to remove.
04 — THE RIGHT ARCHITECTURE

Layer, then map — never copy

The message and the domain both have a place. The trick is to keep them in separate layers and connect them with an explicit mapping, so each can change on its own clock.

Semantic target · stable

Domain ontology

Identity (IRIs), classes, disjointness, roles, cardinalities — driven by competency questions, not by any payload.

▲   lift · validate   ▲

Mapping · the bridge

JSON-LD context · RML / R2RML · SHACL

Lift payloads into the graph and validate incoming messages against domain expectations. Author the context from the ontology down — never the ontology up from arbitrary JSON.

▲   serialize · exchange   ▲

Syntactic contract · volatile

Message schema (JSON / XML)

Free to change for operational reasons — new versions, renamed fields, batching — without touching the conceptual layer.

What each layer looks like

# ✗ Shape dictated by the payload — accidents become axioms
msg:ClaimBatch   a owl:Class .                # an envelope, not a thing
msg:hasItem      a owl:ObjectProperty .       # the "items[]" wrapper
msg:party        a owl:DatatypeProperty ;     # insured embedded as a blob
                 rdfs:range xsd:string .
msg:premium      rdfs:range xsd:decimal .     # money flattened; currency lost
msg:correlationId a owl:DatatypeProperty .   # transport cruft in the domain

# ✓ Shape dictated by the domain — shared identity, typed concepts
claim:Claim        rdfs:subClassOf core:Event ;
                 policy:underPolicy  ?policy .    # link by IRI — one shared Policy
claim:claimant     rdfs:range claim:Claimant .   # a Party in a reified role
coverage:hasPremium rdfs:range coverage:Premium . # Premium ⊑ core:MoneyAmount

The payload still reaches the graph — but as a mapping, not as a schema. An items[] array becomes a set of claim:Claim individuals; a repeated party block resolves to a single IRI; "amount": 500.00 plus a currency code lifts to a core:MoneyAmount. The envelope and the correlation id are simply dropped at the boundary, where they belong.

05 — THE NUANCE

Messages are evidence, not a blueprint

None of this means ignore the messages. They are among your best inputs — just not your template. Mine them for two things:

  • Vocabulary. The nouns and verbs a payload uses are candidate classes and properties. Harvest the terms; that is real signal about what the business exchanges.
  • Coverage. Every field that carries business meaning should have a semantic home in the ontology. Walk the messages to make sure none is orphaned.

And you must eventually connect every message to the model — that is what the mapping layer is for. The line to hold is precise: mine messages for terms; derive structure from the domain.

The one test to apply

Before you add any class or property, ask what question it answers. “How is this transmitted?” → it belongs in the message schema. “What exists, and how is it related?” → it belongs in the ontology. If the only reason a construct exists is that the payload had it, that is not a reason.

The failure mode this avoids

A team wires an ontology straight from an integration schema, ships it, and inherits every future breaking change to that API as a breaking change to their semantics. Worse, the model can’t answer basic domain questions, because it was never shaped by them — only by how one system happened to pack its bytes.