
Building an Operational Ontology: An E-Commerce Walkthrough
The write side of the ontology conversation: named actions, business rules, and write-back to the systems of record — a pattern already running at enterprise scale.
以下正文同步自 Data Engineering Weekly,版权归原站所有,已转换为易读排版。
Reads travel from a shared model to agents, apps, and people. Writes enter through an audited action gate and write back to the systems of record that own the state.
This article is a walkthrough. On top of a single e-commerce scenario, it assembles a model that can carry writes governed by business rules: an operational ontology. Every scene in it runs in a minimal open-source reference implementation (TypeScript, MIT):
https://github.com/gura105/operational-ontology
The morning after the merger, there are two “orders”
Say an e-commerce company acquires a competitor. On Monday morning, the data team is holding two order systems. One keeps its orders in north.tbl_order, the other in south.SALES_ORDER; the schemas don’t line up, and even the status encodings differ. The same “shipped” order is recorded as status = 2 in one system and status = ‘SHIPPED’ in the other.
Inside each system, business runs fine today. The trouble is the questions that cross systems. “Which orders contain this product?” There is nowhere in the company that can answer this simple question with a single query.
For most data engineers, this is not a thought experiment. Acquisitions, business-unit consolidations, SaaS migrations — the same situation keeps coming back. In this article, we will start from this problem and build our way to an ontology — a model of the business on top of the integrated data.
The read side is business as usual
The first move is familiar. A few dozen lines of SQL and a small mapping pull the two order feeds into a single integrated table, aligning column names and unifying status encodings. Ordinary data pipeline work. (Real integration is messier — entity resolution, duplicates, semantic drift — a well-mapped battlefield this walkthrough compresses.)
On top of that integrated table, we now define a model in business vocabulary: Customer, Order, and Product as objects; “a customer has orders” and “an order contains products” as links. Traverse the links, and the earlier question can be answered without caring which system each row came from. The model is materialized into the ontology’s own datastore, indexed from the integrated table — a detail that matters once writes arrive.
Two legacy order systems with different schemas and status encodings are integrated into a unified schema in a few dozen lines of SQL, and Customer, Order, and Product objects with links are modeled on top of it.
Defining entities and relationships to read is the same shape as a semantic layer — familiar territory for readers of this newsletter. The problem is what comes next.
The cancel button is nowhere to be found
A few weeks later, on a dashboard built on this model, an operator spots what appears to be a mistaken order. That is as far as it goes. To cancel it, they close the BI tool and log back into the legacy system. Nowhere on the screen is there a cancel button.
This is the boundary between read and write. OWL/RDF, knowledge graphs, and AI context layers are all legitimate tools for the read side of that boundary — a division of labor, not a ranking. So what do you get if a single design includes both the reading and operating sides from the start? The working example is Palantir Foundry’s Ontology, in production at enterprise scale. In Palantir’s own vocabulary, objects and links are its “semantic” elements, actions and functions its “kinetic” elements — the two halves it is built from. The difference from the read-side tools compresses into one question: can you cancel an order from your semantic layer?
The semantic side of the story was mapped out in this newsletter’s August 14 article. This article continues from there and walks the write side. The reference implementation introduced at the top distills this pattern — the operational ontology — to its essence: what Foundry exhibits, minimized.
Give writes names and rules
The first idea that comes to mind — growing a generic write API on the integrated table — is a bad one. All it does is let anyone fire an UPDATE; nobody defends the business reality that a shipped order cannot be canceled.
This pattern goes the other way. There is no generic update path — absent from the write interface by design — and every business decision gets a named action. The declaration of the action cancelOrder looks like this: the target is the Order object; the parameters are an order ID and a cancellation reason; the precondition is “refuse if already shipped”; the effects are an edit that moves the status to canceled — plus a write-back to the source system (next section).
Run it against a shipped order, and it is refused with a machine-readable error code: SHIPPED_ORDER_CANNOT_BE_CANCELLED. Refusal is a first-class result, not an exception buried in a log. In the same spirit as Result types in Rust or Haskell — failure as a return value, not an exception — a business-rule violation comes back to the caller as a structured value. Run it against an unshipped order, and it succeeds, and the corresponding row in the source order system actually changes. Every attempt, applied or refused, is recorded in the audit log. The cancel button on the dashboard only becomes available once the model has a verb called cancelOrder.
Every caller — human or AI agent — invokes the named action cancelOrder through the same governed gate. The precondition refuses shipped orders with a machine-readable error; an applied call transitions the status. Every attempt, whether applied or refused, is logged in the audit log. A generic UPDATE path is absent by design.
Reads traverse the model freely; writes are decisions, and decisions pass through a gate equipped with rules and an audit trail. This asymmetry is the center of the pattern. Every scene so far runs, in order, in the reference implementation’s pnpm demo.
Declare who owns the truth of every piece of state
Bring in writes, and three questions that a read-only world could leave vague become unavoidable.
First: who holds the Single Source of Truth (SSoT) for each piece of state — its authority?
Every piece of state is declared as one of three kinds. source-backed, like an order’s status: the SSoT lives in the upstream system, and changes propagate back upstream through the governed path of rules and audit. Ontology-owned, like an assignee or a triage note: no legacy system has a column for it, and by declaration the ontology’s own datastore is its SSoT (this is what the demo’s assignOrder writes). Derived, like aggregates: computed, never written. What is forbidden is state left floating with no declared owner.
Second: what happens when write-back fails?
The reference implementation runs write-back before the local commit — the same ordering as Foundry’s write-back webhooks. If the source refuses, nothing changes on the ontology side. The reverse failure — write-back succeeded, local commit failed — remains possible, and that behavior is precisely what must be declared. Declare the failure direction in advance, and it is an engineering problem you can handle at design time. Leave it undeclared, and you discover an outage in production. For source-backed state, the declaration already contains the answer: the source won, and the next re-index reconverges the ontology to its truth.
Third: do changes survive a re-index?
The source order systems keep running, so sooner or later a re-index pulls their new orders back into the ontology’s datastore. When it does, do the changes made through actions — assignments, cancellations — survive? For readers who re-run pipelines daily, this is a pressing question. Cancellations survive by construction — the write-back already changed the source, so a fresh index carries them back. Assignments survive through the overlay: the implementation keeps the source-derived base and the action-derived changes separate, and reapplies the ontology-owned changes onto each freshly indexed base. If a re-index would leave a change floating, its target gone, the re-index itself is refused in its entirety rather than partially applied. That is the minimal implementation’s choice; in production, it would more likely quarantine the orphan and block only its object.
An authority map for an Order object. Status and total are source-backed by the upstream order system and use a governed write-back path. Assignee and Note are ontology-owned, making the ontology datastore their single source of truth. Aggregates and counts are derived, computed only, and never written. State with no declared owner is forbidden.
If write-back sounds like reverse ETL, the difference is the unit of measure. Reverse ETL syncs modeled data downstream on a schedule, with no notion of refusal; a write-back here is one governed decision — rule-checked, audited, refusable — propagating one action’s outcome.
The same gate applies to agents
Everything so far carries over to AI agents unchanged. The list of operations an agent receives is generated by the model: for reads, query tools like search_order are derived from objects and links; for writes, only predefined actions, like cancel_order, are available. No raw SQL is handed over. An agent reads and writes only through the ontology — on the same terms as a human.
When an agent tries to cancel a shipped order, the same precondition that refuses a human refuses it, and
{ “error”: { “code”: “SHIPPED_ORDER_CANNOT_BE_CANCELLED” } }
comes back — a refusal the agent can read, recover from, and explain to its user. The prompt only needs to say what the agent is trying to do. The business rule (“shipped orders cannot be canceled”), the refusal on violation, and the record of every attempt are enforced on the ontology side, so however the prompt is rewritten, the governance does not move. This is the write side’s answer to the question posed by the August 14 article — what an ontology for AI agents actually needs. (Two deliberate omissions: authorization — who may call which action — and concurrency, idempotency included. The preconditions are validity rules, not permissions.)
The parts are old; the placement is new
Not a single component here is an invention: entities, commands, guarded state transitions, and append-only audit logs — the classics of DDD and CQRS. What is new is the placement. And it is not a domain model lifted out of one application’s interior and carried outside unchanged. It sits one level up, different in granularity and nature from any single app’s model: a layer that defines the business’s shared vocabulary on top of data owned by other systems, shared by people, applications, and agents alike. A stack that used to end at visibility becomes a shared domain layer that reaches decisions and actions.
As the minimal implementation shows, none of this depends on a particular product or vendor; it is a free-standing concept. That is exactly why it works as a portable decision criterion. Each time a major platform ships something called an “ontology,” you can ask: is this ontology semantic only, or does it go as far as kinetic — named, rule-carrying actions? And which of the two does your business, or your client’s, actually need? Knowing this concept is the foundation for that call.
And if you try it on your own stack, do not start with a company-wide model. Pick one business operation that a person decides and performs — say, canceling an order: one object, one action, one precondition, an audit log, a write-back. The write side starts there.
Further reading
- The Operational Ontology reference implementation (TypeScript, MIT) — every scene in this article runs in its pnpm demo. Read the code next.
- What an Ontology for AI Agents Actually Needs — the preceding piece, on the semantic side.
正文由 FLUX 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。