AI Architect Trainer Відкрити інтерактивний трек

ГоловнаАрхітектура корпоративної аналітики

Семантична онтологія та моделювання домену

Як рядки таблиць стають бізнес-об'єктами: обмежені контексти й єдина мова домену, RDF/OWL/SPARQL проти графів властивостей, і момент, коли семантичний шар перемагає зоряну схему.

Востаннє переглянуто: 2026-09-04 · In English

У цьому напрямі

Шар 1 дав нам надійні, версійовані, відтворювані таблиці. Але таблиця не знає, що таке «клієнт», «рейс» чи «поліс» — вона знає лише колонки й типи. Шар 2 додає те, чого бракує: типізовану модель домену, спільну для людей, коду й машин. Саме тут система перестає бути базою даних і починає бути операційною системою підприємства — бо об'єкт, на відміну від рядка, має ідентичність, поведінку, зв'язки та політику доступу.

Модуль проходить три рівні: мову й межі (DDD), формалізм і його межі застосовності (RDF/OWL/SPARQL проти графів властивостей) та економіку рішення — коли онтологія справді окупається, а коли зоряна схема лишається правильною відповіддю.

Єдина мова та обмежені контексти: чому канонічна модель завжди програє

E3.1

Найдорожча помилка шару 2 — спроба побудувати одну «канонічну» сутність на все підприємство. У страховій компанії слово «поліс» у продажах означає котирування з чернетками, у врегулюванні збитків — чинний договір з усіма додатковими угодами, у фінансах — потік визнаної виручки. Канонічна таблиця Policy з надмножиною атрибутів формально задовольнить усіх і не буде правдивою для жодного: половина колонок завжди NULL, а семантика кожної залежить від того, хто питає.

Обмежений контекст (bounded context) — це відповідь Domain-Driven Design: межа, всередині якої термін має рівно одне значення. Не «модуль» і не «мікросервіс», а межа значення. Три відділи отримують три моделі «полісу», кожна внутрішньо несуперечлива, а між ними стоїть явна карта контекстів із зафіксованим напрямком залежності.

Чотири конструкції, які роблять це інженерією, а не малюванням:

Два відношення на карті, які варто розрізняти окремо. Спільне ядро — невеликий набір типів, який два контексти володіють спільно й змінюють лише за взаємною згодою; він дешевий, доки лишається малим, і стає найдорожчим місцем системи, щойно розростається. Опублікована мова — контекст оприлюднює стабільний словник для інтеграції, приймаючи на себе зобов'язання версіювати його як публічний 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, графи властивостей і межа відкритого світу

E3.2

Коли межі контекстів прокладені, модель треба записати так, щоб її читала машина. Два промислові формалізми — і вибір між ними майже завжди роблять неправильно.

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 описує обов'язковість. Плутати їх — це намагатися ловити помилки типів компілятором граматики.

Практичне правило вибору:

Ціна виведення. 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 або явній перевірці на завантаженні.

Коли семантичний шар перемагає зоряну схему: дії, зворотний запис і ціна підтримання

E3.3

Зоряна схема Кімбалла — не застаріла технологія, а точний інструмент з чітко окресленою областю застосування: агрегація над узгодженими вимірами для читання. Якщо питання звучить «виручка за регіонами по місяцях», зоряна схема відповість дешевше, швидше й зрозуміліше за будь-яку онтологію. Будувати семантичний шар заради цього — витрачати бюджет, не змінюючи відповіді.

Три ознаки, за яких зоряна схема принципово не працює:

Ціна, про яку мовчать. Онтологія — це не безкоштовна абстракція: під нею стоять зіставлення схем (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 хв.

Антипатерн

Оголосити онтологію «новим сховищем» і мігрувати в неї всю аналітику, включно з чистою агрегацією за узгодженими вимірами. Ви отримуєте ту саму відповідь дорожче, повільніше й із додатковим шаром зіставлень, який тепер треба супроводжувати — і при цьому жодної дії, ідентичності чи політики на зв'язках, заради яких семантичний шар і будувався.

Джерела, з яких виведено напрям

  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

Пройти інтерактивно

У кожного напряму є питання, картки з інтервальним повторенням і облік прогресу. Для них потрібен акаунт — безкоштовний і на одну хвилину.

Відкрити інтерактивний трек Створити безкоштовний акаунт

Далі в цьому треку