Головна › Архітектура корпоративної аналітики
Семантична онтологія та моделювання домену
Як рядки таблиць стають бізнес-об'єктами: обмежені контексти й єдина мова домену, RDF/OWL/SPARQL проти графів властивостей, і момент, коли семантичний шар перемагає зоряну схему.
У цьому напрямі
Шар 1 дав нам надійні, версійовані, відтворювані таблиці. Але таблиця не знає, що таке «клієнт», «рейс» чи «поліс» — вона знає лише колонки й типи. Шар 2 додає те, чого бракує: типізовану модель домену, спільну для людей, коду й машин. Саме тут система перестає бути базою даних і починає бути операційною системою підприємства — бо об'єкт, на відміну від рядка, має ідентичність, поведінку, зв'язки та політику доступу.
Модуль проходить три рівні: мову й межі (DDD), формалізм і його межі застосовності (RDF/OWL/SPARQL проти графів властивостей) та економіку рішення — коли онтологія справді окупається, а коли зоряна схема лишається правильною відповіддю.
Єдина мова та обмежені контексти: чому канонічна модель завжди програє
Найдорожча помилка шару 2 — спроба побудувати одну «канонічну» сутність на все підприємство. У страховій компанії слово «поліс» у продажах означає котирування з чернетками, у врегулюванні збитків — чинний договір з усіма додатковими угодами, у фінансах — потік визнаної виручки. Канонічна таблиця Policy з надмножиною атрибутів формально задовольнить усіх і не буде правдивою для жодного: половина колонок завжди NULL, а семантика кожної залежить від того, хто питає.
Обмежений контекст (bounded context) — це відповідь Domain-Driven Design: межа, всередині якої термін має рівно одне значення. Не «модуль» і не «мікросервіс», а межа значення. Три відділи отримують три моделі «полісу», кожна внутрішньо несуперечлива, а між ними стоїть явна карта контекстів із зафіксованим напрямком залежності.
Чотири конструкції, які роблять це інженерією, а не малюванням:
- Єдина мова домену (ubiquitous language). Один термін на одне поняття — у глосарії, в коді, у схемі й у розмові з бізнесом. Якщо бізнес каже «відвантаження», а клас називається
DeliveryDto, а таблиця —tbl_dlvry, ви вже платите податок на переклад у кожному обговоренні. Мова не документується — вона застосовується через перейменування. - Агрегат. Кластер об'єктів з єдиним коренем і транзакційною межею. Інваріант «сума позицій дорівнює сумі замовлення» тримається лише тоді, коли позиції змінюються виключно через корінь
Order. Агрегат — це не «велика сутність», а відповідь на питання «що має бути узгодженим в одній транзакції». - Антикорупційний шар (anti-corruption layer). Перекладач на межі з чужою моделлю. Мейнфрейм видає
STAT_CD = '07'— ACL перетворює це наClaimStatus.UNDER_REVIEWще до того, як значення торкнеться домену. Без ACL чужа модель просочується всередину й перемагає вашу за пару релізів. - Карта контекстів. Явний перелік відносин: партнерство, постачальник-споживач, відповідність. Незафіксована залежність — це та, яку зламають без попередження.
Два відношення на карті, які варто розрізняти окремо. Спільне ядро — невеликий набір типів, який два контексти володіють спільно й змінюють лише за взаємною згодою; він дешевий, доки лишається малим, і стає найдорожчим місцем системи, щойно розростається. Опублікована мова — контекст оприлюднює стабільний словник для інтеграції, приймаючи на себе зобов'язання версіювати його як публічний API: адитивні зміни, явна застарілість із датою, лише потім видалення. Плутати ці два режими небезпечно: спільне ядро вимагає координації на кожну зміну, опублікована мова — навпаки, звільняє споживачів від координації ціною дисципліни сумісності у власника.
Практичний тест на межу: якщо два підрозділи не можуть узгодити визначення терміна за тридцять хвилин розмови — це не конфлікт людей, це сигнал, що межа контексту проходить саме тут. Проводьте її і йдіть далі. Зворотний тест теж працює: якщо термін означає те саме в усіх обговореннях протягом кварталу, окрема межа для нього не потрібна — не множте контексти там, де мова вже єдина.
# 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,
)
На практиці
Європейський страховик замінив канонічну таблицю Policy на 214 колонок (з них 131 постійно NULL) трьома обмеженими контекстами — Sales, Claims, Finance — із явною картою контекстів. Кількість дефектів «неправильна семантика поля» за квартал впала з 47 до 6, а середній час узгодження нового атрибута — з 11 днів до 2.Антипатерн
«Спочатку побудуємо канонічну модель підприємства, потім усі до неї приєднаються.» Канонічна модель не має власника, тому кожен відділ додає до неї свої поля й ігнорує чужі; через рік це надмножина всіх схем без жодного інваріанта — тобто гірша версія вихідних таблиць, але з ілюзією узгодженості.Формалізм онтології: RDF/OWL/SPARQL, графи властивостей і межа відкритого світу
Коли межі контекстів прокладені, модель треба записати так, щоб її читала машина. Два промислові формалізми — і вибір між ними майже завжди роблять неправильно.
RDF/OWL/SPARQL. Світ як множина триплетів суб'єкт — предикат — об'єкт із глобальними ідентифікаторами (IRI). Сила — у федерації: два підприємства, які ніколи не бачили схем одне одного, можуть посилатися на той самий IRI класу й отримати сумісні дані. OWL додає аксіоми — підкласи, обернені властивості, транзитивність — з яких reasoner виводить нові факти. SPARQL запитує це все, зокрема через шляхи властивостей довільної довжини.
Граф властивостей. Вузли та ребра, у кожного — словник атрибутів. Немає глобальних IRI, немає виведення, зате ребро від початку є першокласним об'єктом із власними властивостями. Мова запитів (Cypher, Gremlin, GQL) ближча до інженерної інтуїції й краще оптимізується на обходах.
Пастка, на якій горять команди — припущення відкритого світу (open-world assumption). OWL вважає, що відсутність факту означає «невідомо», а не «хибно». Аксіома Contract owl:minCardinality 1 hasSignatory не відхилить контракт без підписанта — reasoner спокійно виведе, що підписант існує, просто ще не названий. Це не баг: OWL створювався для інтеграції неповних знань, а не для валідації вводу. Для перевірки «поле мусить бути» потрібен SHACL — окремий формалізм із замкненим світом, який дає звіт про порушення. Онтологія описує значення; SHACL описує обов'язковість. Плутати їх — це намагатися ловити помилки типів компілятором граматики.
Практичне правило вибору:
- Потрібна федерація між організаціями, публікація спільного словника, повторне використання зовнішніх онтологій → RDF/OWL.
- Потрібні швидкі обходи, багаті атрибути на ребрах, керована одним підприємством модель → граф властивостей.
- Потрібне і те, і те → трансляція SPARQL у Cypher переносить більшість шаблонів механічно, але виведення OWL, порожні вузли й відкриті шляхи властивостей прямих відповідників не мають і потребують явного рішення.
Ціна виведення. Reasoning під час запиту легко дає p95 у секундах. Промислова відповідь — матеріалізувати наслідки аксіом у сховищі й оновлювати їх при зміні: запити читають факти, а не виводять їх. Асиметрія тут вирішальна: аксіоми змінюються кілька разів на тиждень, запити йдуть тисячами на годину, тож виведення має відбуватися на рідкій події, а не на частій.
Ідентифікатори понять — це публічний контракт. Щойно IRI класу чи властивості потрапив у запити, конвеєри чи партнерські інтеграції, він перестає бути внутрішньою деталлю. Перейменування на місці ламає споживачів без попередження, тому діє звичайна дисципліна версіювання: новий термін публікується адитивно, старий лишається як застарілий синонім із датою видалення, споживачі мігрують, і тільки потім старий термін зникає. Онтологія, у якій терміни змінюють значення тихо, гірша за відсутність онтології: перша дає неправильні відповіді впевнено, друга не дає жодних.
# 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 ] .
На практиці
Логістичний оператор перевів валідацію вантажів з OWL-обмежень на SHACL і виявив, що 4.1% рейсів у сховищі не мали пункту відправлення — три роки reasoner мовчки виводив, що пункт «існує, але не названий». Матеріалізація наслідків аксіом замість виведення під час запиту знизила p95 SPARQL-запитів з 9.4 с до 310 мс.Антипатерн
Використовувати OWL-обмеження кардинальності як перевірку якості даних і повідомляти бізнесу «онтологія гарантує заповненість». Вона нічого не гарантує: у відкритому світі відсутність факту — це «невідомо». Валідація належить SHACL або явній перевірці на завантаженні.Коли семантичний шар перемагає зоряну схему: дії, зворотний запис і ціна підтримання
Зоряна схема Кімбалла — не застаріла технологія, а точний інструмент з чітко окресленою областю застосування: агрегація над узгодженими вимірами для читання. Якщо питання звучить «виручка за регіонами по місяцях», зоряна схема відповість дешевше, швидше й зрозуміліше за будь-яку онтологію. Будувати семантичний шар заради цього — витрачати бюджет, не змінюючи відповіді.
Три ознаки, за яких зоряна схема принципово не працює:
- Потрібна дія, а не звіт. Аналітик має не лише побачити ризиковану поставку, а натиснути «затримати відвантаження», щоб рішення записалося, отримало автора й час і пішло назад в ERP. Факт-таблиця доступна лише для читання за конструкцією; вимір не має поведінки. Онтологія додає типізовані дії з передумовами й зворотним записом.
- Ідентичність важливіша за агрегат. Питання «чи це той самий контрагент, що й у санкційному списку» не має сенсу в зоряній схемі: сурогатний ключ виміру існує в межах сховища, а не в межах світу. Об'єкт має стабільну ідентичність, до якої можна прив'язати резолюцію сутностей і провенанс.
- Зв'язок сам несе атрибути й політику. Зовнішній ключ каже лише «пов'язано». Першокласний тип зв'язку має власну ідентичність, властивості (дата початку, роль, джерело твердження, довіра) і власні права доступу. Саме тому «хто затвердив цей зв'язок» — питання, на яке зоряна схема відповісти не може, а онтологія може.
Ціна, про яку мовчать. Онтологія — це не безкоштовна абстракція: під нею стоять зіставлення схем (900 таблиць у 60 типів об'єктів) і матеріалізовані подання. Промислові матчери дають ранжовані гіпотези, а не істину, і на масштабі підприємства їхня точність різко падає — тому робочий процес завжди «автоматичний матчер пропонує, доменний експерт підтверджує». А матеріалізовані графові подання над мільярдними таблицями не можна перераховувати щоночі: потрібне інкрементне підтримання подань, яке проштовхує дельти замість повного перерахунку.
Правило власності. Онтологія — це проєкція, а не друге джерело істини. Дія пише транзакційно в систему-джерело запису, а онтологія перевиводить свій стан із неї. Інакше через півроку ви маєте дві розбіжні правди й нічний job, який вирішує конфлікти за принципом «хто останній».
Дії мусять бути ідемпотентними. Мережа обірветься між записом у систему-джерело й підтвердженням, користувач натисне кнопку вдруге, черга доставить повідомлення повторно — це не крайні випадки, а нормальний режим роботи. Тому кожна дія має нести ключ ідемпотентності, а повторне застосування з тим самим ключем зобов'язане повертати попередній результат, а не створювати друге затримання відвантаження. Без цього перша ж мережева помилка перетворює аудит рішень на художній вимисел.
Як рахувати окупність. Витрати семантичного шару складаються з трьох статей: разове зіставлення схем, постійне підтримання матеріалізованих подань і супровід словника як публічного API. Вигода вимірюється лише в одиницях рішень: скільки дій ухвалюється в системі за тиждень і скільки часу раніше йшло на з'ясування «чи це той самий контрагент». Якщо перелік дій порожній, окупності немає — і це нормальна, чесна відповідь, а не поразка.
# 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 }
На практиці
Виробник промислового обладнання лишив зоряну схему для 40 фінансових дашбордів і побудував онтологію лише навколо 6 операційних рішень (затримка відвантаження, перепланування ТО, ескалація дефекту). Зіставлення 870 вихідних таблиць у 54 типи об'єктів дало матчер із подальшим підтвердженням експертами: автоматичні пропозиції прийняли у 61% випадків, решту виправили вручну. Перехід матеріалізованих подань на інкрементне підтримання скоротив нічне вікно з 6 год 20 хв до 11 хв.Антипатерн
Оголосити онтологію «новим сховищем» і мігрувати в неї всю аналітику, включно з чистою агрегацією за узгодженими вимірами. Ви отримуєте ту саму відповідь дорожче, повільніше й із додатковим шаром зіставлень, який тепер треба супроводжувати — і при цьому жодної дії, ідентичності чи політики на зв'язках, заради яких семантичний шар і будувався.Джерела, з яких виведено напрям
- 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
Пройти інтерактивно
У кожного напряму є питання, картки з інтервальним повторенням і облік прогресу. Для них потрібен акаунт — безкоштовний і на одну хвилину.
Відкрити інтерактивний трек Створити безкоштовний акаунт