Ontology engineering · RDFS · OWL · SHACL

Three languages, three jobs.

RDFS, OWL and SHACL are not rival ways to do the same thing, and not rungs on a ladder of “power.” They answer different questions. Pick the wrong one and you write axioms that never fire, or constraints that never catch anything. A guideline for choosing — and for using all three together.

Written for Modelers Data engineers deciding where a rule belongs

The distinction that settles most of it

Almost every “should this be RDFS, OWL or SHACL?” question resolves once you separate two intentions that people routinely confuse. One is describing what your terms mean — and what a reasoner may therefore infer. The other is describing what your data must look like to be accepted — and rejecting it when it doesn’t. RDFS and OWL do the first; SHACL does the second.

The reason you cannot swap them is a single semantic fact: RDFS and OWL are open-world and monotonic. A fact you did not state is unknown, never false; adding data can only add conclusions, never retract them. SHACL is closed-world over its targets: a fact you did not state is absent, and absence can fail a check. Ask OWL to reject missing data and it shrugs; ask SHACL to infer new facts and it can’t.

The whole guideline in two lines

Reasoning adds facts you didn’t state. Validation rejects data you don’t want. Model meaning in RDFS/OWL; enforce quality in SHACL — and never ask one to do the other’s job.

the vocabulary layer

RDFS

Name your terms and relate them: classes, properties, a subclass hierarchy, labels. Just enough logic for lightweight subsumption. The floor everything stands on.

the meaning layer

OWL

Say precisely what a class is: restrictions, cardinality, disjointness, property characteristics. A reasoner then classifies, infers and checks consistency.

the validation layer

SHACL

State what conformant data must look like: required fields, counts, datatypes, value sets — and get a violation report against the actual instances.

They stack. Most mature graphs use RDFS vocabulary inside an OWL ontology for meaning, and a separate SHACL layer for quality — exactly the shape of this Chubb repository, whose fnd/ and ins/ modules are OWL 2 DL and whose shapes/ directory validates the reasoned data. The three sections below take each in turn, then a worked example runs one requirement through all three.

RDFS LAYER 1 — RDF SCHEMA

Name things, and relate them

RDFS is the smallest useful schema language. It gives you a vocabulary and a little inference — and for a surprising number of jobs, that is all you need.

  • Classes and properties. rdfs:Class, rdf:Property — declare that a term exists.
  • Hierarchy. rdfs:subClassOf and rdfs:subPropertyOf give transitive subsumption: a Policy is an Agreement is an InformationEntity.
  • Domain and range. Typed endpoints for properties — but as inference rules, not checks (see the trap below).
  • Human annotation. rdfs:label, rdfs:comment — the labels every tool and query relies on.

Reach for RDFS alone when you need a shared taxonomy, readable labels and simple “is-a” reasoning, and you do not need automated classification, consistency checking or data validation. A controlled vocabulary, a lightweight reference model, a glossary with structure — RDFS carries them without the weight of a DL reasoner.

# RDFS gives a term a name, a place in the hierarchy, and a typed range.
policy:Policy      a rdfs:Class ; rdfs:subClassOf agr:Agreement ;
                  rdfs:label "Policy"@en .
policy:policyNumber a rdf:Property ; rdfs:label "policy number" ;
                  rdfs:range xsd:string .   # any value is inferred to be a string

The trap: rdfs:domain / rdfs:range are not constraints. Declaring rdfs:domain policy:Policy does not forbid using the property elsewhere — it concludes that whatever you used it on is a policy. If you meant “only allowed on a Policy,” that is a SHACL sh:targetClass shape, not a domain. (This is AP-10.)

OWL LAYER 2 — WEB ONTOLOGY LANGUAGE

Say exactly what you mean

OWL is a description logic. Where RDFS lets you name a class, OWL lets you define one precisely enough that a reasoner can decide membership, derive new relationships, and prove the whole model is consistent.

  • Class expressions. intersectionOf, unionOf, complementOf — build a class from others (Chubb’s ContractWithTerm is one).
  • Restrictions. someValuesFrom (∃), allValuesFrom (∀), hasValue, and cardinality — the heart of a definition.
  • Property characteristics. Functional, InverseFunctional, Transitive, Symmetric, inverseOf — semantics a reasoner exploits.
  • Equivalence and disjointness. equivalentClass drives classification; disjointWith gives the reasoner something to falsify.

Reach for OWL when you need any of: individuals classified automatically from the properties they carry; a consistency check that a modeling clash actually fails; inferred links (inverses, transitivity, role-fillers); or formal semantics for interoperability. Chubb reasons under the OWL 2 DL profile — expressive but decidable; keep functional and cardinality axioms on simple properties or you silently leave it.

# OWL defines Policy: at most one policy number, and a functional property.
policy:policyNumber a owl:DatatypeProperty , owl:FunctionalProperty .
policy:Policy rdfs:subClassOf
    [ a owl:Restriction ; owl:onProperty policy:policyNumber ;
      owl:maxCardinality 1 ] .

# Disjointness gives the reasoner a contradiction to catch.
[] a owl:AllDisjointClasses ; owl:members ( core:Person core:Organization ) .

The trap: OWL infers, it never rejects. Under the open-world assumption a missing value is unknown, so maxCardinality 1 will not flag a policy with zero numbers, and asserting two distinct values does not error — with no unique-name assumption the reasoner infers they are owl:sameAs. If you wanted “required” or “these are two different things,” OWL is the wrong tool. (See AP-5 and AP-6.)

SHACL LAYER 3 — SHAPES CONSTRAINT LANGUAGE

Promise what the data must look like

SHACL is where “required,” “exactly one,” “must match this pattern” and “drawn from this list” finally mean what you expect. A shape targets a set of nodes and states constraints they must satisfy; a SHACL engine reports every violation against the actual data.

  • Shapes and targets. sh:NodeShape with sh:targetClass (or sh:targetNode) says which nodes to check.
  • Cardinality and type. sh:minCount / sh:maxCount, sh:datatype, sh:class, sh:nodeKind — presence, count and kind.
  • Value rules. sh:pattern, sh:minInclusive, sh:in (a fixed value set, e.g. over a SKOS scheme).
  • Severity and messages. sh:Violation / sh:Warning / sh:Info and a human sh:message — hard rules and soft “shoulds,” each explained.

Reach for SHACL when you must accept or reject data: required business keys, ingestion gates, referential and format checks, completeness reports. It closes the world exactly where you need it closed — and nowhere else.

# SHACL requires a policy number, and warns (not fails) on missing coverage.
shpolicy:PolicyShape a sh:NodeShape ; sh:targetClass policy:Policy ;
  sh:property [ sh:path policy:policyNumber ; sh:datatype xsd:string ;
                sh:minCount 1 ; sh:maxCount 1 ;
                sh:message "A policy must have exactly one policyNumber." ] ;
  sh:property [ sh:path coverage:hasCoverage ; sh:minCount 1 ;
                sh:severity sh:Warning ;
                sh:message "A policy should provide at least one coverage." ] .

Order matters: validate against the merged, reasoned graph, not the raw triples. Run the OWL reasoner first so inferred types and links exist, then run SHACL — that is why Chubb’s shapes header reads “validate against the merged, reasoned graph.” Reason to add meaning; validate to enforce quality.

ONE REQUIREMENT, THREE LANGUAGES

“A policy has a policy number”

The same business sentence lands differently in each language — and only one of them makes it required. This is the actual axiom set from this repository: RDFS declares the property, OWL bounds it, SHACL enforces it.

RDFS declares the term — says nothing about how many
policy:policyNumber a rdf:Property ;
    rdfs:label "policy number" ;
    rdfs:range xsd:string .   # values are strings; that is all
OWL bounds it — “at most one,” but never “must have one”
policy:policyNumber a owl:DatatypeProperty , owl:FunctionalProperty .
policy:Policy rdfs:subClassOf
    [ owl:onProperty policy:policyNumber ; owl:maxCardinality 1 ] .
# A missing number is fine (unknown). Two distinct → inferred sameAs.
SHACL enforces it — required, typed, or the record fails
shpolicy:PolicyShape a sh:NodeShape ; sh:targetClass policy:Policy ;
    sh:property [ sh:path policy:policyNumber ; sh:datatype xsd:string ;
        sh:minCount 1 ; sh:maxCount 1 ] .
# A missing number is a VIOLATION, reported against the data.

Read the three together. RDFS gives the number a name and a type. OWL says a policy cannot have two — a statement of meaning a reasoner can use, but it will never complain about none. Only SHACL’s sh:minCount 1 makes the number required and turns its absence into a reported error. Three layers, one requirement, no redundancy: each says the part only it can say.

THE DECISION

Which one, for the rule in front of you

Two questions settle almost every case. First: do you want to add facts or reject data? Adding is RDFS/OWL; rejecting is SHACL. Second, if you are adding: how much must the meaning carry? Labels and hierarchy are RDFS; definitions a reasoner can act on are OWL.

DimensionRDFSOWLSHACL
Its jobname & relate termsdefine meaning preciselyvalidate data shape
Question it answerswhat is this, what’s above it?what can be inferred? is it consistent?is this data acceptable?
World assumptionopenopenclosed (over targets)
A missing value meansunknownunknownabsent → a violation
Effect on the graphinfers a littleinfers a lotreports — changes nothing
Can it reject bad data?nonoyes
Expressivitysubclass, domain, range, labelsrestrictions, cardinality, disjoint, characteristicscount, datatype, pattern, class, in, node kind
Never use it to…reject incomplete datainfer new facts
Toolingany RDF storeDL reasoner (HermiT, Pellet, ELK)SHACL engine (pySHACL, Jena, TopBraid)

Or just look up the rule you have:

You want to…Reach for
Give a term a label and a place in a hierarchyRDFS
Infer that something is a Policy from the properties it carriesRDFS domain / OWL
Classify individuals automatically into a defined classOWL equivalentClass
Prove the model is logically consistentOWL + reasoner
Say Person and Organization cannot overlapOWL disjointWith
Derive an inverse or a transitive chainOWL property characteristics
Require every Policy to carry a policy numberSHACL minCount
Reject a claim that is missing its loss dateSHACL
Constrain a code to a fixed set of valuesSHACL sh:in (over a SKOS scheme)
Enforce a date or identifier formatSHACL sh:pattern / sh:datatype
Flag a soft “should” without failing the recordSHACL sh:severity sh:Warning
THE ARCHITECTURE

They layer, they don’t compete

The mature setup is not “pick one.” It is all three, each confined to its job, over one graph — which is exactly how this repository is arranged:

  • RDFS inside OWL — the fnd/ and ins/ modules use RDFS for labels, hierarchy and annotation, and OWL for the definitions that make the model reason. One T-Box, two levels of commitment.
  • SHACL alongside — the shapes/ directory mirrors each module (policy.shapes.ttl, claim.shapes.ttl…) and enforces the quality rules OWL cannot: required keys, exact counts, referential checks, soft warnings.
  • A pipeline, in order — load and merge the modules, run the DL reasoner to materialise inferences, then run SHACL over the reasoned graph. Meaning first, quality gate second.

✓ The rule of thumb, for a review

When a rule shows up, ask what kind it is. Does it state what a term means or what may be inferred? Put it in RDFS if it is only a name or a hierarchy, in OWL if a reasoner should act on it. Does it say what data is acceptable — required, bounded, formatted, referential? That is SHACL, every time. If you find yourself writing an OWL cardinality hoping it will reject a record, you have reached for the wrong layer.

RDFS draws the vocabulary, OWL gives it meaning a machine can reason over, and SHACL keeps the data worthy of both. Kept in their lanes, they compound. Blurred together — OWL used as a validator, SHACL used as a schema of meaning — each does its own job badly and undoes the other’s.