Уявіть: ви отримали технічне завдання. Замовник хоче платформу для прослуховування аудіокниг. Є користувачі, є книги, є автори, є якийсь «прогрес прослуховування» і «особисті колекції». Перший інстинкт більшості розробників — відкрити IDE та почати писати класи. Або, у кращому разі, відкрити pgAdmin і почати малювати таблиці.
Це помилка.
Не тому що код чи таблиці є чимось поганим. А тому, що занадто раннє занурення у деталі реалізації — це будівництво будинку, починаючи зі стін, без фундаменту і без архітектурного плану. Стіни може й встоять, але кімнати виявляться незручними, а перебудова згодом обійдеться значно дорожче.
Перше, що повинен зробити розробник або архітектор системи, — зрозуміти предметну область. Скласти «карту бізнесу»: що існує у цьому світі, як ці речі пов'язані між собою, які правила керують їх взаємодією. Лише після цього модель можна перенести на конкретні технології: реляційну базу даних, документо-орієнтовану, графову чи будь-яку іншу.
Цей процес і є концептуальним моделюванням (Conceptual Modeling) — темою цієї статті.
Перш ніж заглибитись у деталі, варто окреслити загальну картину. Проектування бази даних відбувається на трьох рівнях абстракції, і розуміння різниці між ними є критично важливим.
Концептуальний рівень — найвища, найабстрактніша точка зору. На цьому рівні ми не думаємо про таблиці, стовпці чи типи даних. Ми думаємо про сутності реального світу (автор, аудіокнига, користувач) та зв'язки між ними (автор написав аудіокнигу, користувач слухає аудіокнигу). Концептуальна модель — це мова домену, однаково зрозуміла розробнику та бізнес-аналітику, який ніколи не писав SQL.
Логічний рівень — перехід від бізнес-концепцій до формальних структур даних обраної моделі (реляційної, документо-орієнтованої, графової). Тут з'являються таблиці, первинні та зовнішні ключі, процес нормалізації.
Фізичний рівень — конкретна реалізація: DDL-скрипти, індекси, типи даних конкретної СУБД, внутрішня організація зберігання на диску.
Цей модуль пройде всі три рівні послідовно, використовуючи платформу аудіокниг як наскрізний приклад. Ця стаття присвячена виключно першому рівню.
Термін «концептуальна модель» (Conceptual Model, або Conceptual Schema) у контексті баз даних точніше за все описується так: це формальне представлення знань про предметну область, незалежне від будь-якої технології зберігання. Ключові слова тут — «формальне» та «незалежне».
«Формальне» означає, що модель не є вільним текстом чи набором розмитих понять. Вона описується за допомогою чіткого набору конструкцій з визначеною семантикою: сутностей, атрибутів та зв'язків. Це дозволяє перетворювати її на конкретні схеми алгоритмічно, без двозначностей.
«Незалежне» означає, що у концептуальній моделі немає жодних слідів технічних рішень. Немає VARCHAR(64), немає FOREIGN KEY, немає ON DELETE CASCADE. Є лише бізнес-поняття та їхні зв'язки.
Архітектор, проектуючи будинок, спочатку малює концептуальний план (ескіз): де кімнати, де коридори, де вхід, де вікна. Цей план зрозумілий замовнику і не містить деталей про марку цегли, тип бетону чи характеристики кабелів електропроводки.
Лише після затвердження концепції архітектор переходить до технічних креслень: конструктивних схем, специфікацій матеріалів, розрахунків навантажень. Це аналог логічного та фізичного рівнів.
Якщо замовник захоче змінити розташування стіни — це легко зробити на рівні ескізу. Але якщо будівництво вже почалося і несучу стіну вже зведено — переробка коштуватиме на порядок більше.
Те саме справедливо для баз даних.
Концептуальна модель
Що це не є
INT, VARCHAR)Найвідомішим і найширше розповсюдженим методом концептуального моделювання є діаграми «сутність-зв'язок» (Entity-Relationship Diagrams, або ER-діаграми). Метод запропонований Пітером Ченом (Peter Chen) у 1976 році у статті «The Entity-Relationship Model — Toward a Unified View of Data» — одній із найбільш цитованих робіт в історії інформатики.
Метод ґрунтується на трьох ключових поняттях: сутність (Entity), атрибут (Attribute) та зв'язок (Relationship). Розглянемо кожен з них детально на прикладі нашої аудіоплатформи.
Сутність (Entity) — це будь-який чітко ідентифікований об'єкт або явище реального світу, що має значення для системи, що розробляється. Кожна сутність може існувати у багатьох екземплярах (instances), і кожен екземпляр повинен бути відмінним від інших.
Зверніть увагу на слово «ідентифікований»: якщо не можна однозначно відрізнити один екземпляр від іншого — це не сутність у моделі ER. Наприклад, «літр води в океані» чи «кілограм пшениці з поля» не є сутностями в цьому розумінні, бо ми не можемо ідентифікувати конкретний літр або конкретний кілограм.
Щоб виявити сутності, аналізують опис предметної області, документацію, інтерв'ю з замовником. Іменники у тексті вимог — перший орієнтир, хоча й не єдиний критерій.
Бізнес-вимоги нашої аудіоплатформи:
Виділимо кандидатів у сутності:
| Кандидат | Чи є сутністю? | Обґрунтування |
|---|---|---|
| Аудіокнига | ✅ Так | Має унікальний ідентифікатор, назву, тривалість |
| Автор | ✅ Так | Має ім'я, біографію; існує незалежно від книг |
| Жанр | ✅ Так | Має назву та опис; до одного жанру належать багато книг |
| Користувач | ✅ Так | Унікальний логін, пароль, аватар |
| Колекція | ✅ Так | Має назву, дату створення; належить конкретному користувачу |
| Файл аудіокниги | ✅ Так | Різні формати (MP3, FLAC) одного запису — окремі файли |
| Прогрес прослуховування | ⚠️ Особливий | Пов'язує Користувача та Аудіокнигу, має власні атрибути |
| Назва аудіокниги | ❌ Ні | Це атрибут сутності «Аудіокнига» |
| Формат файлу | ❌ Ні | Атрибут сутності «Файл аудіокниги» (MP3, FLAC тощо) |
«Прогрес прослуховування» — цікавий випадок: це не просто зв'язок між двома сутностями, а зв'язок, що має власні дані (позиція та дата останнього прослуховування). У теорії ER це моделюється як зв'язок з атрибутами (Relationship with Attributes), що ми розглянемо у розділі про зв'язки.
В ER-моделі розрізняють сильні (Strong Entities) та слабкі (Weak Entities) сутності.
Сильна сутність (Strong Entity) — існує самостійно і може бути однозначно ідентифікована власними атрибутами. У нашій платформі сильними є: Author, Genre, User.
Слабка сутність (Weak Entity) — не може існувати без прив'язки до іншої сутності (її «власника») і не може бути однозначно ідентифікована без посилання на неї. Слабкі сутності нашої платформи:
AudiobookFile — не може існувати без Audiobook. Якщо аудіокнигу видалено, усі її файли зникають разом з нею. Файл ідентифікується через комбінацію власного ідентифікатора та ідентифікатора батьківської аудіокниги.Collection — не може існувати без User. Колекція без власника позбавлена сенсу.Слабкість сутності має пряме практичне значення: при виборі стратегії каскадного видалення (ON DELETE CASCADE) ми спираємося саме на факт залежності слабкої сутності від сильної. Більш детально ця тема розкривається у статті про фізичну схему.
Атрибут (Attribute) — це характеристика або властивість сутності, що описує або уточнює її. Кожен екземпляр сутності має конкретне значення кожного атрибуту (або спеціальне значення «невизначено» — NULL).
Класифікація атрибутів є важливою, оскільки різні типи атрибутів по-різному відображаються на наступних рівнях моделювання.
Простий атрибут (Simple Attribute) — неподільна одиниця: duration (тривалість аудіокниги в секундах), release_year (рік виходу).
Складений атрибут (Composite Attribute) — що складається з кількох компонентів, кожен з яких має самостійне значення. Класичний приклад — Name (повне ім'я), що складається з FirstName і LastName. У нашій аудіоплатформі автор (Author) має саме такий атрибут.
Name як єдиного рядка чи розбиттям на first_name + last_name — це архітектурне рішення. Якщо прізвище авторів буде використовуватися для сортування, пошуку або виведення в форматі «Доренко Іван» — розбивати обов'язково. Якщо ж ім'я завжди виводиться цілком і не аналізується — можна зберігати рядком. Концептуальна модель дозволяє позначити Name складеним, а на логічному рівні прийняти остаточне рішення.Ключовий атрибут (Key Attribute) — атрибут або група атрибутів, що однозначно ідентифікує кожен екземпляр сутності. У нотації Пітера Чена ключові атрибути підкреслюються.
Для кожної сутності нашої платформи:
| Сутність | Ключовий атрибут | Обґрунтування |
|---|---|---|
Author | id (UUID) | Сурогатний ключ; ім'я не є унікальним |
Genre | id (UUID) | Назва жанру теж могла б бути ключем (вона унікальна), але UUID надійніший |
Audiobook | id (UUID) | Назва неунікальна (можуть бути книги з однаковою назвою різних авторів) |
User | id (UUID) | username також унікальний — альтернативний ключ |
Collection | id (UUID) | Назва неунікальна навіть для одного користувача |
AudiobookFile | id (UUID) | Один файл для однієї книги |
Вибір між природними (натуральними) та сурогатними ключами — тема, яка детально розкривається у статті про логічне моделювання. На концептуальному рівні достатньо позначити, що кожна сутність має механізм ідентифікації.
Похідний атрибут (Derived Attribute) — значення якого обчислюється з інших атрибутів. Наприклад, якщо знаємо duration (тривалість у секундах), то duration_hours (тривалість у годинах) є похідним атрибутом. Зберігати похідні атрибути в базі — зазвичай надмірність. На ER-діаграмі вони позначаються пунктирним овалом.
Багатозначний атрибут (Multivalued Attribute) — атрибут, що може мати кілька значень для одного екземпляру сутності. Наприклад, якби платформа підтримувала кілька мов озвучення однієї аудіокниги — language став би багатозначним атрибутом. У нашій поточній моделі це реалізовано через окрему сутність AudiobookFile, де кожен файл може мати свій формат.
id (ключовий) — унікальний ідентифікаторName (складений) → FirstName, LastNameBio (простий, допускає NULL) — біографіяImagePath (простий, допускає NULL) — шлях до фотоid (ключовий)Name (простий, обов'язковий, унікальний) — назва жанруDescription (простий, допускає NULL) — опис жанруid (ключовий)Title (простий, обов'язковий) — назваDuration (простий, обов'язковий) — тривалість у секундахReleaseYear (простий, обов'язковий) — рік виходуDescription (простий, допускає NULL) — описCoverImagePath (простий, допускає NULL) — шлях до обкладинкиid (ключовий)Username (простий, обов'язковий, унікальний) — логінPasswordHash (простий, обов'язковий) — хеш пароляEmail (простий, допускає NULL)AvatarPath (простий, допускає NULL)id (ключовий)Name (простий, обов'язковий) — назва колекціїCreatedAt (простий, обов'язковий) — дата створенняid (ключовий)FilePath (простий, обов'язковий) — шлях до файлуFormat (простий, обов'язковий) — mp3, flac, wav тощоSize (простий, допускає NULL) — розмір у байтахЗв'язок (Relationship) — асоціація або залежність між двома чи більше сутностями, що відображає факти предметної області. Якщо сутності — це «іменники» бізнесу, то зв'язки — це «дієслова»: автор написав книгу, користувач слухає книгу, колекція містить аудіокниги.
Кожен зв'язок характеризується двома ключовими параметрами: кардинальністю та модальністю.
Кардинальність (Cardinality) визначає кількісне співвідношення між екземплярами двох сутностей у зв'язку. Розрізняють три базові типи:
Один до одного (1:1)
Кожному екземпляру сутності A відповідає не більше одного екземпляра сутності B, і навпаки.
Рідкісний тип. Зазвичай свідчить про те, що дві сутності можна об'єднати в одну. Виправданий, коли потрібно логічно відокремити частини різної природи (наприклад, публічний профіль користувача та його приватні налаштування безпеки).
Один до багатьох (1:N)
Одному екземпляру A відповідає нуль або більше екземплярів B. Один екземпляр B відповідає рівно одному A.
Найпоширеніший тип. Автор — Аудіокниги: один автор написав багато книг.
Багато до багатьох (M:N)
Одному A відповідає нуль або більше B. Одному B відповідає нуль або більше A.
Вимагає розв'язання при переході на логічний рівень — через проміжну (junction) таблицю. Колекція — Аудіокниги: колекція містить багато книг, книга може бути в багатьох колекціях.
Модальність (Modality), або участь (Participation), визначає, чи є участь сутності у зв'язку обов'язковою. Розрізняють:
У нотації Crow's Foot модальність позначається символами на кінці лінії зв'язку:
|oРозберемо кожен зв'язок докладно, формулюючи бізнес-правила:
Author → Audiobook (writes)
Genre → Audiobook (categorizes)
User → Collection (owns)
Collection ↔ Audiobook (includes)
audiobook_collection.Audiobook → AudiobookFile (consists of)
User ↔ Audiobook через ListeningProgress (listens to)
position, last_listened). Цей зв'язок особливий — він є не просто проміжною таблицею, а повноцінною сутністю-зв'язком із власними даними.ListeningProgress — це зв'язок-з-атрибутами (Relationship with Attributes). На логічному рівні він стане окремою таблицею listening_progresses, що матиме власний первинний ключ та атрибути. Це дуже поширений патерн: коли M:N-зв'язок «обростає» власними даними, він фактично перетворюється на повноцінну сутність.Тепер ми можемо зобразити повну концептуальну модель. Використовуємо нотацію Crow's Foot як більш наочну для відображення кардинальності та модальності:
Концептуальні моделі можна зображати у різних нотаціях — стандартизованих системах позначень. Розглянемо дві найпоширеніші.
Оригінальна нотація, запропонована у фундаментальній статті Пітера Чена. Є академічним стандартом і широко використовується у підручниках та наукових роботах.
Основні позначення:
Перевага: дуже виразна, показує атрибути сутностей і зв'язків безпосередньо на діаграмі. Недолік: займає значно більше місця, стає нечитабельною для великих схем.
Запропонована Гордоном Евересом (Gordon Everest) під назвою «Inverted Arrow» («обернена стрілка»). Пізніше отримала назву Crow's Foot («вороняча лапка») або Fork («виделка») через вигляд символу множинності.
Основні позначення:
| Символ | Значення |
|---|---|
| (одна риска) | Один |
{ (три риски / лапка) | Багато |
o (кружечок) | Нуль (необов'язково) |
| (вертикальна риска) | Один (обов'язково) |
На діаграмі Чена зв'язок між Author та Audiobook виглядав би так:
[Author] —— <writes> —— [Audiobook]
| ||
(Id) (Id)
(Name) (Title)
(Bio) (Duration)
Ромб <writes> позначає зв'язок, а лінії мають мітки 1 (з боку Author) і M (з боку Audiobook).
У нотації Crow's Foot:
AUTHOR ||--o{ AUDIOBOOK : "написав"
Читається так: «Рівно один Author (||) до нуля або більше (o{) Audiobook».
Символ || — обов'язковий одиничний кінець (кожна книга має рівно одного автора).
Символ o{ — необов'язковий множинний кінець (автор може мати нуль або більше книг).
ER-модель підтримує концепції узагальнення (Generalization) та спеціалізації (Specialization) — механізми, аналогічні успадкуванню в об'єктно-орієнтованому програмуванні.
Супертип (Supertype) — загальна сутність, що об'єднує спільні атрибути. Підтип (Subtype) — спеціалізована сутність, що успадковує атрибути супертипу та додає власні.
Уявімо, що наша платформа планує ввести преміум-підписку. Тоді User стає супертипом, а FreeUser та PremiumUser — підтипами:
id, username, email (успадковані від User)PremiumUser додатково має subscription_expires (власний атрибут)FreeUser може мати обмежений доступ до каталогуУ поточній схемі аудіоплатформи ієрархій немає, тому концептуальна модель залишається плоскою. Але знання про їх існування важливе: воно нагадує, чому концептуальний рівень агностичний — один і той же концепт (успадкування ролей) може реалізовуватися принципово по-різному залежно від обраної технології.
Дано опис системи:
«Університет проводить курси. Кожен курс веде один викладач. Студенти можуть записуватися на курси. За результатами курсу студент отримує оцінку.»
Завдання:
Дано опис системи доставки їжі:
«Ресторани пропонують страви у різних категоріях (піца, суші, бургери). Клієнти роблять замовлення. У кожному замовленні може бути кілька страв у різній кількості. Кожне замовлення доставляє один кур'єр.»
Завдання:
Ви отримали завдання побудувати систему управління проектами (спрощений аналог Jira):
Завдання:
Концептуальне моделювання — це перший і найважливіший крок у проектуванні будь-якої системи зберігання даних. Його мета — побудувати чітку, технологічно-незалежну карту предметної області, зрозумілу усім учасникам проекту.
Ключові поняття, яких ми торкнулися:
ListeningProgress).У наступній статті ми перейдемо на логічний рівень: трансформуємо побудовану ER-модель аудіоплатформи у реляційну схему, розглянемо правила відображення зв'язків на таблиці та детально вивчимо вибір первинних ключів.