Home › Enterprise Intelligence Architecture
Semantic Ontology & Domain Modeling
How table rows become business objects: bounded contexts and ubiquitous language, RDF/OWL/SPARQL versus property graphs, and the point where a semantic layer beats a star schema.
In this area
Layer 1 gave us reliable, versioned, reproducible tables. But a table does not know what a 'customer', a 'voyage' or a 'policy' is — it knows columns and types. Layer 2 supplies what is missing: a typed domain model shared by people, code and machines. This is where a system stops being a database and starts being an enterprise operating system, because an object — unlike a row — carries identity, behaviour, relationships and policy.
The module works through three levels: language and boundaries (DDD), formalism and its limits (RDF/OWL/SPARQL versus property graphs), and the economics of the decision — when an ontology genuinely pays for itself, and when a star schema is still the right answer.
Ubiquitous Language & Bounded Contexts: Why the Canonical Model Always Loses
The most expensive mistake at layer 2 is trying to build one 'canonical' entity for the whole enterprise. In an insurer, 'policy' means a quote with drafts to the sales desk, an in-force contract with endorsements to claims, and a stream of recognised revenue to finance. A canonical Policy table holding the superset of attributes will formally satisfy everyone and be true for no one: half the columns are permanently NULL, and the meaning of each depends on who is asking.
The bounded context is Domain-Driven Design's answer: a boundary inside which a term has exactly one meaning. Not a module, not a microservice — a boundary of meaning. Three departments get three models of 'policy', each internally consistent, with an explicit context map between them that fixes the direction of dependency.
Four constructs that make this engineering rather than drawing:
- Ubiquitous language. One term per concept — in the glossary, the code, the schema and the conversation. If the business says 'shipment' while the class is
DeliveryDtoand the table istbl_dlvry, you are paying a translation tax in every discussion. Language is not documented; it is enforced by renaming. - Aggregate. A cluster of objects with one root and a transactional boundary. The invariant 'line totals equal order total' holds only if lines are modified exclusively through the
Orderroot. An aggregate is not 'a big entity' — it is the answer to 'what must be consistent within a single transaction'. - Anti-corruption layer. A translator at the boundary with a foreign model. The mainframe emits
STAT_CD = '07'; the ACL turns it intoClaimStatus.UNDER_REVIEWbefore the value ever touches your domain. Without an ACL the foreign model seeps inward and wins within two releases. - Context map. An explicit list of relationships: partnership, customer-supplier, conformist. An unrecorded dependency is the one that gets broken without notice.
Two map relationships worth separating explicitly. A shared kernel is a small set of types two contexts own jointly and change only by mutual agreement; it is cheap while it stays small and becomes the most expensive place in the system the moment it grows. A published language is a context releasing a stable vocabulary for integration, taking on the obligation to version it like a public API: additive changes, dated deprecation, removal only afterwards. Confusing the two is dangerous: a shared kernel demands coordination on every change, whereas a published language frees consumers from coordination at the price of compatibility discipline in the owner.
A practical boundary test: if two departments cannot agree on a term's definition in thirty minutes of conversation, that is not a people problem — it is the signal that a context boundary runs exactly there. Draw it and move on. The inverse test holds too: if a term has meant the same thing in every discussion for a quarter, it does not need a boundary of its own — do not multiply contexts where the language is already shared.
# Context map enforced in code: three bounded contexts, one anti-corruption layer.
# The legacy mainframe model NEVER crosses the boundary untranslated.
from dataclasses import dataclass
from enum import Enum
class ClaimStatus(Enum): # Ubiquitous language of the CLAIMS context
SUBMITTED = "submitted"
UNDER_REVIEW = "under_review"
SETTLED = "settled"
DENIED = "denied"
# Legacy codes are data, not vocabulary. They live only inside the ACL.
_LEGACY_STATUS = {
"03": ClaimStatus.SUBMITTED,
"07": ClaimStatus.UNDER_REVIEW,
"11": ClaimStatus.SETTLED,
"19": ClaimStatus.DENIED,
}
@dataclass(frozen=True)
class Claim: # CLAIMS context aggregate root
claim_id: str
policy_ref: str # a REFERENCE into the sales context, not a copy
status: ClaimStatus
reserve_eur: int
def acl_translate(row: dict) -> Claim:
"""Anti-corruption layer. Fails loudly on unknown legacy codes instead of
letting an untranslated value leak into the domain as a magic string."""
code = str(row["STAT_CD"]).zfill(2)
if code not in _LEGACY_STATUS:
raise ValueError("unmapped legacy STAT_CD=%r; extend the ACL, not the domain" % code)
return Claim(
claim_id=row["CLM_NO"].strip(),
policy_ref=row["POL_NO"].strip(),
status=_LEGACY_STATUS[code],
reserve_eur=int(row["RSRV_AMT_CENTS"]) // 100,
)
In practice
A European insurer replaced a canonical 214-column Policy table (131 columns permanently NULL) with three bounded contexts — Sales, Claims, Finance — and an explicit context map. Defects classified as 'wrong field semantics' fell from 47 to 6 per quarter, and the mean time to agree a new attribute dropped from 11 days to 2.The anti-pattern
'First we build the canonical enterprise model, then everyone conforms to it.' A canonical model has no owner, so every department appends its own fields and ignores the rest; a year later it is the superset of all schemas with no invariants — a worse version of the source tables, wearing the costume of agreement.Ontology Formalism: RDF/OWL/SPARQL, Property Graphs and the Open-World Boundary
Once context boundaries are drawn, the model must be written down so a machine can read it. There are two industrial formalisms — and the choice between them is made wrongly most of the time.
RDF/OWL/SPARQL. The world as a set of subject — predicate — object triples with global identifiers (IRIs). Its strength is federation: two enterprises that have never seen each other's schemas can reference the same class IRI and get interoperable data. OWL adds axioms — subclasses, inverse properties, transitivity — from which a reasoner derives new facts. SPARQL queries all of it, including property paths of arbitrary length.
Property graph. Nodes and edges, each carrying a dictionary of attributes. No global IRIs, no entailment — but an edge is a first-class object with its own properties from the start. The query languages (Cypher, Gremlin, GQL) sit closer to engineering intuition and optimise traversals better.
The trap that burns teams — the open-world assumption. OWL treats an absent fact as 'unknown', not 'false'. The axiom Contract owl:minCardinality 1 hasSignatory will not reject a contract with no signatory — the reasoner cheerfully concludes a signatory exists and merely has not been named. This is not a bug: OWL was designed to integrate incomplete knowledge, not to validate input. To enforce 'this field must be present' you need SHACL, a separate closed-world formalism that returns a violation report. An ontology states meaning; SHACL states obligation. Confusing them is like expecting a grammar checker to catch type errors.
A practical selection rule:
- Cross-organisation federation, publishing a shared vocabulary, reusing external ontologies → RDF/OWL.
- Fast traversals, rich edge attributes, a model governed by a single enterprise → property graph.
- Both → SPARQL-to-Cypher translation carries most patterns mechanically, but OWL entailment, blank nodes and open-ended property paths have no direct equivalent and demand an explicit decision.
The price of inference. Reasoning at query time comfortably produces p95 latencies in seconds. The industrial answer is to materialise the entailments into the store and refresh them on change, so queries read facts instead of deriving them. The asymmetry is decisive here: axioms change a few times a week, queries arrive by the thousand per hour, so inference belongs on the rare event, not the frequent one.
Concept identifiers are a public contract. Once a class or property IRI has reached queries, pipelines or partner integrations, it stops being an internal detail. Renaming in place breaks consumers without notice, so ordinary versioning discipline applies: the new term is published additively, the old survives as a deprecated alias with a removal date, consumers migrate, and only then does the old term disappear. An ontology whose terms change meaning quietly is worse than no ontology at all: the first gives wrong answers confidently, the second gives none.
# Two formalisms, two jobs. OWL states MEANING; SHACL states OBLIGATION.
@prefix ex: <https://ontology.acme.example/logistics#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
### --- OWL: what the terms MEAN (open world) -------------------------------
ex:Shipment a owl:Class .
ex:Facility a owl:Class .
ex:Warehouse a owl:Class ; rdfs:subClassOf ex:Facility .
ex:departsFrom a owl:ObjectProperty ;
rdfs:domain ex:Shipment ; rdfs:range ex:Facility .
ex:receivedBy a owl:ObjectProperty ;
owl:inverseOf ex:departsFrom . # entailment: fills the reverse direction
# NOTE: this axiom does NOT reject a shipment without an origin.
# Open-world: "no stated origin" means UNKNOWN, never FALSE.
ex:Shipment rdfs:subClassOf
[ a owl:Restriction ;
owl:onProperty ex:departsFrom ;
owl:minCardinality "1"^^xsd:nonNegativeInteger ] .
### --- SHACL: what the data MUST satisfy (closed world) --------------------
ex:ShipmentShape a sh:NodeShape ;
sh:targetClass ex:Shipment ;
sh:property [
sh:path ex:departsFrom ;
sh:minCount 1 ; # THIS is what fails the load
sh:class ex:Facility ;
sh:message "Shipment must declare exactly one origin facility." ] ;
sh:property [
sh:path ex:grossWeightKg ;
sh:datatype xsd:decimal ;
sh:minExclusive 0 ] .
In practice
A logistics operator moved shipment validation from OWL restrictions to SHACL and discovered that 4.1% of voyages in the store had no origin facility — for three years the reasoner had silently concluded the origin 'exists but is unnamed'. Materialising entailments instead of deriving them at query time cut SPARQL p95 from 9.4 s to 310 ms.The anti-pattern
Using OWL cardinality restrictions as a data-quality gate and telling the business 'the ontology guarantees completeness'. It guarantees nothing: under the open world, an absent fact is 'unknown'. Validation belongs to SHACL or to an explicit load-time check.When a Semantic Layer Beats a Star Schema: Actions, Writeback and the Cost of Maintenance
The Kimball star schema is not obsolete technology; it is a precise tool with a clearly bounded domain: aggregation over conformed dimensions, for reading. If the question is 'revenue by region by month', a star schema answers it more cheaply, faster and more legibly than any ontology. Building a semantic layer for that is spending budget without changing the answer.
Three signatures where a star schema structurally cannot work:
- An action is required, not a report. The analyst must not merely see a risky consignment but press 'hold shipment', so that the decision is recorded, attributed, timestamped and pushed back into the ERP. A fact table is read-only by construction; a dimension has no behaviour. An ontology adds typed actions with preconditions and writeback.
- Identity matters more than aggregation. 'Is this the same counterparty as the one on the sanctions list?' is meaningless in a star schema: a dimension's surrogate key exists inside the warehouse, not inside the world. An object has stable identity to which entity resolution and provenance can be attached.
- The relationship itself carries attributes and policy. A foreign key says only 'related'. A first-class link type has its own identity, properties (start date, role, asserting source, confidence) and its own access rules. That is exactly why 'who approved this relationship' is answerable in an ontology and unanswerable in a star schema.
The cost nobody quotes. An ontology is not a free abstraction: underneath it sit schema matching (900 source tables into 60 object types) and materialised views. Industrial matchers produce ranked hypotheses, not truth, and their precision degrades sharply at enterprise scale — so the working process is always 'the matcher proposes, the domain expert confirms'. And materialised graph views over billion-row tables cannot be recomputed nightly: they need incremental view maintenance, propagating deltas instead of full recomputation.
The ownership rule. The ontology is a projection, not a second source of truth. An action writes transactionally to the system of record, and the ontology re-derives its state from it. Otherwise, six months later you own two divergent truths and a nightly job that resolves conflicts by last-write-wins.
Actions must be idempotent. The network will drop between the write to the system of record and the acknowledgement, the user will press the button twice, the queue will redeliver the message — these are not edge cases but the normal operating regime. So every action must carry an idempotency key, and re-applying it with the same key must return the prior result rather than creating a second shipment hold. Without this, the first network error turns the decision audit into fiction.
How to compute the payback. A semantic layer's cost has three lines: one-off schema matching, ongoing maintenance of materialised views, and stewardship of the vocabulary as a public API. The benefit is measurable only in units of decisions: how many actions are taken in the system per week, and how much time previously went into establishing 'is this the same counterparty'. If the list of actions is empty, there is no payback — and that is an honest, normal answer rather than a defeat.
# Ontology object + link + ACTION. The action is what a star schema cannot express:
# preconditions, an authored decision, and a transactional write to the system of record.
objectTypes:
- apiName: Shipment
primaryKey: shipment_id # a WORLD identity, not a warehouse surrogate key
backingDataset: fabric.logistics.shipment_v3
properties:
shipment_id: { type: string, policy: public }
eta_utc: { type: timestamp }
declared_value: { type: decimal, policy: restricted } # property-level policy
status: { type: enum, values: [planned, in_transit, held, delivered] }
linkTypes:
- apiName: carriedBy # first-class: the EDGE carries attributes
from: Shipment
to: Carrier
cardinality: MANY_TO_ONE
properties:
assigned_at: { type: timestamp }
asserted_by: { type: string } # provenance lives on the relationship
confidence: { type: double }
actions:
- apiName: holdShipment
appliesTo: Shipment
parameters:
reason_code: { type: enum, values: [sanctions_hit, doc_mismatch, damage] }
note: { type: string, maxLength: 500 }
preconditions: # evaluated against the ontology, server-side
- expr: "object.status == 'in_transit'"
message: "Only an in-transit shipment can be held."
- expr: "actor.hasRole('logistics.controller')"
message: "Requires the logistics controller role."
writeback:
target: erp.sap.shipment_status # SYSTEM OF RECORD owns the write
mode: transactional # ontology re-derives; it is a projection
onSuccess: { set_status: held, emit_event: shipment.held.v1 }
In practice
An industrial-equipment manufacturer kept the star schema for 40 financial dashboards and built an ontology only around 6 operational decisions (hold shipment, reschedule maintenance, escalate defect). Mapping 870 source tables into 54 object types used a matcher with expert confirmation: 61% of automatic proposals were accepted, the rest corrected by hand. Switching materialised views to incremental maintenance cut the nightly window from 6 h 20 m to 11 m.The anti-pattern
Declaring the ontology 'the new warehouse' and migrating all analytics into it, including pure aggregation over conformed dimensions. You get the same answer more expensively, more slowly, and with an extra mapping layer to maintain — while gaining none of the actions, identity or edge-level policy the semantic layer was built for.Sources this area derives from
- A Domain Model of Oil Pipelines An Exerc
- A Systems Thinking Approach to Domain Dr
- A Systematic Mapping Study on Microservi
- A Systematic Process for Domain Engineer
- Expressive Reasoning Graph Store A Unified Framework for M
- S2CTrans Building a bridge from SPARQL to Cypher
- Killing Two Birds with One Stone Querying Property Graphs
- A reference ontology for digital scienti
- The Role of Schema Matching in Large Enterprises
- Incremental View Maintenance for Property Graph Queries
- Meta Property Graphs Extending Property Graphs with Metada
- A Domain Model Framework for Engineering
Work through it interactively
Every area has questions, spaced-repetition cards and a progress record. Those need an account, which is free and takes a moment.
Open the interactive track Create a free account