Tables and columns describe how data is stored. An ontology describes what your data means - the entities (Patient, Train, Region), the relationships between them (a Train serves a Region; a Patient is admitted to a Hospital), and the hierarchies that flow through both. This module gives every downstream agent and Copilot experience a shared semantic vocabulary.
Patient, Train, Region) and bind them to your Lakehouse + Eventhouse data.Up to this point you've built a complete data stack: a real-time Eventhouse, a Lakehouse, a Direct Lake semantic model. Every consumer - dashboards, reports, agents, Copilot - still has to know which table holds which fact and how to join them. That overhead is paid on every new question.
A Fabric Ontology removes that overhead. You define the conceptual model once - "a Patient has Vitals, is admitted to a Hospital, and lives in a Region. A Train runs on a Line and serves Regions" - and bind each entity to the tables that already exist in OneLake. Every Data Agent, M365 Copilot prompt, and Foundry orchestrator you build downstream then speaks the same vocabulary.
Below is the entity-relationship sketch for this lab. It spans both customer domains
(health, transit) plus a shared Region entity that stitches them together.
That shared entity is what enables cross-domain questions in Module 9.
Region entity is the shared anchor that makes Module 9's
cross-domain queries possible.
From the Fabric portal, navigate back to urbanpulse-{your-user-id}. By now you should have: a Lakehouse, a Mirrored DB, an Eventhouse, a Real-Time Dashboard, and a Direct Lake semantic model.
Click + New item → search "Ontology" → pick Ontology (preview).
Name it: ont_urbanpulse → Create.
ont_urbanpulseAn entity type is "the conceptual thing" plus a binding to where its data actually lives.
Patient entityIn the Ontology editor, click + Add entity type. Configure:
Patienteh_urbanpulse_rti → HospitalVitalspatient_idage, gender, condition, diagnosis_codeHospitalVitals KQL tableTrain the same wayRepeat the pattern with this binding:
| Entity | Source | Identity | Properties |
|---|---|---|---|
Train |
Eventhouse → TrainTelemetry |
trainId |
line, status |
Two entities don't come from the streaming KQL tables. They're logical, bound to existing tables already in the Lakehouse from Module 2:
Hospital → Lakehouse lh_urbanpulse_bronze → mirrored SQL table HospitalsRegion → Lakehouse regions table (mirrored from Cosmos)lh_urbanpulse_bronze from Module 2's
mirror + shortcut steps - no extra setup required. If you skipped Module 2,
the rest of the ontology still works without these two entities.
The Ontology editor's left rail should now show 4 entity types: Patient, Train, Hospital, Region. Click any one to see its properties and source binding.
This is where an ontology earns its keep. You're declaring the meaningful connections between entities so a downstream agent can traverse them without having to know SQL joins.
In the Ontology editor, click + Add relationship and create these three:
| From | Name | To | Cardinality |
|---|---|---|---|
Patient |
admitted_to |
Hospital |
many-to-one |
Train |
on_line |
Line (or store as a string property if you skipped Line as an entity) |
many-to-one |
These cross-domain relationships are what enable Module 9 to answer questions spanning hospitals and transit without bespoke SQL:
| From | Name | To | Why it matters |
|---|---|---|---|
Hospital |
in_region |
Region |
"How's hospital load in this region?" |
Line |
serves_region |
Region |
"Are trains for this region delayed?" |
Regionont_urbanpulse in your workspace.Patient, Train, Hospital, Region - bound to your Eventhouse + Lakehouse data.Region anchor.ont_urbanpulse and declare "ground on
Patient + Vitals." The agent's instructions become shorter, more accurate, and
portable across customers.