AI Architect Trainer Open the interactive track

HomeEnterprise 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.

Last reviewed: 2026-09-04 · Українською

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

E3.1

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:

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

E3.2

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:

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

E3.3

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:

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

  1. A Domain Model of Oil Pipelines An Exerc
  2. A Systems Thinking Approach to Domain Dr
  3. A Systematic Mapping Study on Microservi
  4. A Systematic Process for Domain Engineer
  5. Expressive Reasoning Graph Store A Unified Framework for M
  6. S2CTrans Building a bridge from SPARQL to Cypher
  7. Killing Two Birds with One Stone Querying Property Graphs
  8. A reference ontology for digital scienti
  9. The Role of Schema Matching in Large Enterprises
  10. Incremental View Maintenance for Property Graph Queries
  11. Meta Property Graphs Extending Property Graphs with Metada
  12. 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

Continue in this track