Проектування баз даних

Модуль "Проектування реляційних баз даних" для 04.java/pr2

Модуль "Проектування реляційних баз даних" для 04.java/pr2

Мета

Створити цикл із 8 глибоких, академічних статей у директорії content/04.java/pr2/, що проведе читача від першої ідеї предметної області до готового SQL-скрипта з міграціями Flyway. Наскрізний приклад — платформа аудіокниг (Authors, Audiobooks, Genres, Users, Collections, Listening Progress).

!IMPORTANT
Вимоги до стилю та обсягу:

  • Академічний, книжковий стиль — формальний, але живий; текст-перше, код-ілюстрація.
  • Кожна стаття ~ 1000–1200 рядків Markdown.
  • Писати частинами по ~100 рядків, а не всю статтю одразу. Після кожного блоку — перевірка, корекція, продовження.
  • Суворе дотримання prompt.md (Why Before How, Scaffolding, Text First, Code Anatomy, No Silent Code).
  • Активне використання Docus-компонентів із DOCUS_COMPONENTS.md.
  • Концептуальна модель — технологічно-агностична (не прив'язана до реляційної парадигми).

Структура модуля (8 статей)

Стаття 1. Концептуальне моделювання: Мистецтво розуміння предметної області

Файл: content/04.java/pr2/01.conceptual-modeling.md
Обсяг: ~1000–1200 рядків

Зміст

  1. Вступ та мотивація (~100 рядків)
    • Hook: "Ви отримали технічне завдання: побудувати платформу аудіокниг. З чого почати? З коду? З бази? Ні — з розуміння бізнесу."
    • Чому проектування починається не з CREATE TABLE, а з розмови з бізнесом.
    • Три рівні абстракції даних (концептуальний → логічний → фізичний) як огляд модуля.
    • ::mermaid — піраміда трьох рівнів абстракції.
  2. Що таке концептуальна модель і чому вона агностична (~120 рядків)
    • Визначення: концептуальна модель — це карта бізнесу, а не проект бази даних.
    • Чому одну й ту саму модель можна реалізувати в PostgreSQL, MongoDB, Neo4j або навіть в JSON-файлах.
    • ::card-group — порівняння: "Одна модель → різні реалізації" (SQL, Document, Graph).
    • Аналогія з архітектурним кресленням будинку: архітектурний план vs план електропроводки.
  3. Сутності (Entities): Виявлення об'єктів реального світу (~150 рядків)
    • Формальне визначення сутності. Що є сутністю, що не є (критерій ідентифікації).
    • Практика: аналіз ТЗ аудіоплатформи → виділення сутностей: Author, Audiobook, Genre, User, Collection, Audiobook File, Listening Progress.
    • Сильні (Strong) vs Слабкі (Weak) сутності: чому Audiobook File залежить від Audiobook.
    • ::plant-uml — Chen-нотація: сутності аудіоплатформи (на основі schema.txt).
    • ::tip — Практичний критерій: "Якщо можна скласти список екземплярів — це сутність."
  4. Атрибути (Attributes): Деталізація сутностей (~120 рядків)
    • Прості, складені (Composite), похідні (Derived), багатозначні (Multivalued) атрибути.
    • Ключові атрибути (Key Attributes) — механізм ідентифікації.
    • Приклад: Author.Name як складений атрибут (FirstName + LastName).
    • ::plant-uml — Chen-нотація з атрибутами для 2–3 сутностей.
  5. Зв'язки (Relationships): Бізнес-правила між сутностями (~200 рядків)
    • Що таке зв'язок та його ступінь (Degree).
    • Кардинальність (Cardinality): 1:1, 1:N, M:N — як читати бізнес-правила.
    • Модальність (Participation): повна (Total) vs часткова (Partial).
    • Зв'язки з атрибутами: Listening_Progress як зв'язок-з-даними між User та Audiobook.
    • Повний розбір усіх зв'язків аудіоплатформи:
      • Author 1:N Audiobook (writes)
      • Genre 1:N Audiobook (categorizes)
      • User 1:N Collection (owns)
      • Collection M:N Audiobook (includes)
      • Audiobook 1:N Audiobook_File (consists of)
      • User M:N Audiobook через Listening_Progress
    • ::mermaid — erDiagram (спрощена Crow's Foot) для аудіоплатформи.
  6. Нотації ER-діаграм: Chen vs Crow's Foot (~120 рядків)
    • Історія: Peter Chen (1976) — академічний стандарт.
    • Порівняння з Crow's Foot (Gordon Everest) — індустріальний стандарт.
    • ::tabs — одна й та сама модель у двох нотаціях (Chen через ::plant-uml, Crow's Foot через ::mermaid).
    • ::note — "У цьому курсі ми далі використовуватимемо Crow's Foot як найпоширенішу в індустрії."
  7. Ієрархії та спеціалізація (Supertype/Subtype) (~80 рядків)
    • Коли одна сутність є підтипом іншої (приклад: PremiumUser як підтип User — гіпотетичне розширення).
    • ::warning — "На концептуальному рівні це природно, але на реляційному рівні вимагатиме окремих рішень."
  8. Практичні завдання (~80 рядків)
    • ::steps — завдання 3 рівнів:
      • Рівень 1: Визначити сутності та атрибути для "Системи бронювання готелів".
      • Рівень 2: Побудувати ER-діаграму для "Онлайн-магазину".
      • Рівень 3: Спроектувати концептуальну модель для "Системи управління проектами" з обґрунтуванням кожного зв'язку.
  9. Підсумок (~30 рядків)

Стаття 2. Логічне моделювання: Від бізнес-ідей до структур даних

Файл: content/04.java/pr2/02.logical-modeling.md
Обсяг: ~1000–1200 рядків

Зміст

  1. Вступ: Навіщо потрібен логічний рівень (~80 рядків)
    • Міст між бізнес-мовою та технічною реалізацією.
    • ::mermaid — flowchart: Conceptual → Logical → Physical.
  2. Реляційна модель: Основні поняття (~150 рядків)
    • Відношення (Relation), кортеж (Tuple), атрибут (Attribute), домен (Domain).
    • Коли відношення стає таблицею: 5 правил інтерпретації.
    • ::note — Математична основа (Codd, 1970) vs практична інтерпретація.
  3. Трансформація ER → Логічна схема: Правила відображення (~250 рядків)
    • Правило 1: Сутність → Таблиця (ключові атрибути → PK).
    • Правило 2: Зв'язок 1:N → Зовнішній ключ (FK) на стороні "багато".
    • Правило 3: Зв'язок M:N → Допоміжна (Join) таблиця.
    • Правило 4: Зв'язок 1:1 → FK з UNIQUE або злиття таблиць.
    • Правило 5: Атрибут зв'язку → атрибут join-таблиці.
    • ::steps — покрокове відображення аудіоплатформи.
    • ::plant-uml — логічна схема (на основі PlantUML з schema.txt, секція @startuml).
  4. Первинні ключі: Натуральні vs Сурогатні (~150 рядків)
    • Натуральні ключі: ISBN, email — переваги та ризики.
    • Сурогатні ключі: UUID, SERIAL — чому UUID став стандартом для розподілених систем.
    • ::tabs — порівняння: Natural Key vs Surrogate Key.
    • ::warning — "Задайте собі питання: чи може це значення змінитися? Якщо так — воно не може бути PK."
  5. Зовнішні ключі та посилальна цілісність (~120 рядків)
    • FK як "стрілка", що показує на PK іншої таблиці.
    • Каскадні дії: ON DELETE CASCADE vs SET NULL vs RESTRICT — коли що використовувати.
    • Приклад: Що стається з audiobooks, коли видаляється author?
  6. Обмеження цілісності (Integrity Constraints) (~100 рядків)
    • Сутнісна цілісність (Entity Integrity): PK ≠ NULL.
    • Посилальна цілісність (Referential Integrity): FK → існуючий PK.
    • Доменна цілісність (Domain Integrity): CHECK, UNIQUE, NOT NULL.
    • Приклади з аудіоплатформи: duration > 0, UNIQUE(username), release_year в допустимому діапазоні.
    • ::card-group — три типи цілісності як картки.
  7. Практичні завдання (~80 рядків)
    • Рівень 1: Перетворити задану ER-діаграму у логічну схему.
    • Рівень 2: Визначити всі обмеження цілісності для заданої логічної схеми.
    • Рівень 3: Спроектувати логічну модель для нового домену з обґрунтуванням вибору ключів.
  8. Підсумок (~50 рядків)

Стаття 3. Нормалізація: Гігієна даних та боротьба з аномаліями

Файл: content/04.java/pr2/03.normalization.md
Обсяг: ~1000–1200 рядків

Зміст

  1. Вступ: Чому "працює" ≠ "правильно" (~80 рядків)
    • Hook: Уявіть таблицю, де ім'я автора дублюється в кожному рядку аудіокниги — що буде, якщо автор змінить прізвище?
    • Три аномалії: вставки, оновлення, видалення.
  2. Функціональні залежності (Functional Dependencies) (~120 рядків)
    • Визначення: X → Y означає, що X однозначно визначає Y.
    • Повна vs часткова залежність. Транзитивна залежність.
    • Приклади на аудіоплатформі.
  3. Перша нормальна форма (1НФ) (~120 рядків)
    • Визначення: атомарність значень.
    • Антиприклад: зберігання кількох жанрів в одному полі через кому.
    • ::tabs — "До 1НФ" vs "Після 1НФ".
  4. Друга нормальна форма (2НФ) (~120 рядків)
    • Визначення: немає часткових залежностей від складеного ключа.
    • Приклад: audiobook_collection з додатковим полем audiobook_title — порушення. Декомпозиція.
    • ::mermaid — схема декомпозиції.
  5. Третя нормальна форма (3НФ) (~120 рядків)
    • Визначення: немає транзитивних залежностей.
    • Приклад: якщо б в audiobooks зберігались author_name і author_bio — це транзитивна залежність через author_id.
    • Декомпозиція → виділення authors.
  6. Нормальна форма Бойса-Кодда (НФБК/BCNF) (~100 рядків)
    • Суворіша форма за 3НФ. Коли 3НФ недостатньо. Приклад з перекриваючими ключами.
    • ::note — "На практиці НФБК збігається з 3НФ у 99% випадків."
  7. Свідома денормалізація (~150 рядків)
    • Коли нормалізація суперечить продуктивності.
    • Приклад: кешування audiobook_count в таблиці collections.
    • ::warning — "Денормалізація — це свідомий, задокументований компроміс, а не лінь."
    • ::card-group — "Коли нормалізувати" vs "Коли денормалізувати".
  8. Перевірка схеми аудіоплатформи (~80 рядків)
    • Аудит кожної таблиці на відповідність 3НФ. Обґрунтування.
  9. Практичні завдання (~80 рядків)
  10. Підсумок (~30 рядків)

Стаття 4. Фізична схема: Від абстракції до DDL

Файл: content/04.java/pr2/04.physical-schema.md
Обсяг: ~1000–1200 рядків

Зміст

  1. Вступ: Матеріалізація абстракції (~70 рядків)
    • Що саме відбувається на фізичному рівні: типи даних, обмеження, індекси.
    • Чому одна логічна схема може мати кілька фізичних реалізацій (SQLite, PostgreSQL, H2).
  2. SQL як мова визначення даних (DDL) (~100 рядків)
    • CREATE TABLE, ALTER TABLE, DROP TABLE.
    • Стиль коду SQL: naming conventions (snake_case, множина для таблиць).
    • Посилання на SQL Style Guide.
  3. Типи даних як перша лінія оборони (~150 рядків)
    • Чому duration INTEGER а не VARCHAR — типізація запобігає помилкам.
    • UUID vs SERIAL/IDENTITY — коли що обирати.
    • ENUM типи: file_format_enum для форматів аудіофайлів.
    • ::tabs — порівняння типів даних між H2, SQLite, PostgreSQL (на основі SQL-файлів з temp/pr2/sql).
  4. Обмеження (Constraints) на практиці (~200 рядків)
    • PRIMARY KEY: синтаксис та внутрішня робота.
    • FOREIGN KEY: повний синтаксис з ON DELETE / ON UPDATE.
    • UNIQUE: CONSTRAINT genres_name_key UNIQUE (name).
    • CHECK: CHECK (duration > 0), CHECK (release_year >= 1900 ...).
    • NOT NULL vs NULL: філософія "чи може це поле бути порожнім?"
    • ::code-group — DDL для кожної таблиці аудіоплатформи (H2 SQL з ddl_h2.sql).
  5. Індекси: Прискорення доступу (~120 рядків)
    • Що таке індекс (аналогія: алфавітний покажчик у книзі).
    • Коли створювати: FK-поля, поля пошуку, поля сортування.
    • CREATE INDEX audiobooks_author_id_idx ON audiobooks(author_id) — навіщо саме тут.
    • ::warning — "Індекс прискорює читання, але уповільнює запис."
  6. DML: Наповнення бази тестовими даними (~120 рядків)
    • INSERT INTO — синтаксис з повними прикладами (на основі dml_h2.sql).
    • Порядок вставки: спочатку довідники → потім залежні таблиці → потім зв'язки.
    • ::code-collapse — повний DML-скрипт аудіоплатформи.
  7. Повна фізична схема аудіоплатформи (~80 рядків)
    • ::plant-uml — фінальна ER-діаграма (Crow's Foot) з усіма таблицями, ключами, обмеженнями.
  8. Практичні завдання (~80 рядків)
  9. Підсумок (~30 рядків)

Стаття 5. Архітектурна класифікація таблиць

Файл: content/04.java/pr2/05.table-classification.md
Обсяг: ~1000–1200 рядків

Зміст

  1. Вступ: Не всі таблиці однакові (~80 рядків)
    • Hook: "База даних із 15 таблиць. Чи можна до всіх ставитися однаково? Ні — у кожної є своя «вдача»."
    • Навіщо класифікувати: різні стратегії кешування, індексації, архівації.
  2. Статичні довідники (Static Lookups) (~150 рядків)
    • Визначення: дані, що не змінюються або змінюються вкрай рідко.
    • Приклад з аудіоплатформи: genres — 5–20 записів, задаються адміністратором.
    • Стратегії в додатку: кешування на старті, Enum в Java як дзеркало.
    • ::note — "Статичний довідник — це «словник» вашого домену."
  3. Динамічні довідники / Стрижневі таблиці (Core/Master Tables) (~150 рядків)
    • Визначення: основний масив інформації, що активно росте.
    • Приклади: authors, audiobooks, users — стрижень бізнесу.
    • Чому їх називають "Master Data": це сутності, навколо яких будується решта.
    • Стратегії: повнотекстовий пошук, пагінація, м'яке видалення (Soft Delete).
  4. Статично-динамічні довідники (~100 рядків)
    • Визначення: рідко змінювані дані, що можуть мати FK.
    • Приклад: таблиця тарифних планів (якби в аудіоплатформі була підписка).
    • Відмінність від статичних: наявність зовнішніх ключів і дат оновлення.
  5. Таблиці-Зв'язки (Junction/Association Tables) (~150 рядків)
    • Визначення: існують лише для розв'язання M:N-зв'язків.
    • Приклад: audiobook_collection — не містить бізнес-даних, лише FK.
    • Складений первинний ключ vs сурогатний ID.
    • Коли таблиця-зв'язка "дорослішає": додавання атрибутів (дата додавання до колекції → перетворення на довідник-зв'язку).
    • ::mermaid — еволюція таблиці-зв'язки.
  6. Транзакційні / Подієві таблиці (Transaction/Event Tables) (~150 рядків)
    • Визначення: фіксують факти, події, дії користувачів.
    • Приклад: listening_progress — часто записується, рідко читається цілком.
    • Характеристики: інтенсивний INSERT, потенційне слабке місце продуктивності.
    • Стратегії: партиціонування за датою, TTL (Time-To-Live), архівація.
    • ::warning — "Транзакційна таблиця без стратегії архівації — це тикающий годинник."
  7. Довідник-зв'язка (Hybrid: Reference + Association) (~100 рядків)
    • Визначення: містить FK та власні несправочні дані (коментарі, суми, тощо).
    • Приклад (гіпотетичний): таблиця reviews (user_id FK, audiobook_id FK, rating, comment).
    • Чому повноцінною зв'язкою назвати не можна, а довідником — теж.
  8. Зведена таблиця класифікації (~80 рядків)
    • ::card-group або таблиця — всі типи з прикладами аудіоплатформи, частотою зміни, стратегіями кешування/архівації.
    • ::mermaid — mindmap або дерево класифікації.
  9. Практичні завдання (~80 рядків)
  10. Підсумок (~30 рядків)

Стаття 6. Еволюція схеми: Database Migrations з Flyway

Файл: content/04.java/pr2/06.database-migrations.md
Обсяг: ~1000–1200 рядків

Зміст

  1. Вступ: Чому "один SQL-файл" — це тупик (~100 рядків)
    • Hook: "Ви запустили додаток у production. Через тиждень потрібно додати поле rating до audiobooks. Як ви це зробите? Відредагуєте ddl.sql і запустите знову? А що з даними користувачів?"
    • Проблема: ручне керування схемою неможливо масштабувати.
    • Концепція "Database as Code".
  2. Що таке міграції бази даних (~100 рядків)
    • Визначення: послідовність версійованих скриптів, що трансформують схему.
    • Аналогія: Git для бази даних.
    • ::mermaid — timeline: V1 (initial) → V2 (add rating) → V3 (add reviews table).
  3. Flyway: Основи (~150 рядків)
    • Що таке Flyway, чому він став стандартом для Java-проектів.
    • Принцип роботи: таблиця flyway_schema_history, послідовне виконання.
    • Конвенція іменування: V1__Create_authors_table.sql, V2__Create_genres_table.sql.
    • ::code-tree — структура директорії міграцій.
  4. Практика: Перетворення DDL аудіоплатформи на міграції (~200 рядків)
    • Розбиття монолітного ddl_h2.sql на версійовані міграції V1–V8.
    • ::steps — покрокове створення кожної міграції.
    • ::code-group — кожна міграція як окрема вкладка.
  5. Seed Data: Міграції для початкових даних (~100 рядків)
    • Коли INSERT'и є частиною міграцій (статичні довідники → V9__Seed_genres.sql).
    • Коли ні (тестові дані → окремий механізм).
  6. Еволюція схеми: Типові сценарії (~150 рядків)
    • Додавання нового стовпця: ALTER TABLE audiobooks ADD COLUMN rating DECIMAL(3,1).
    • Додавання нової таблиці: CREATE TABLE reviews (...).
    • Зміна типу стовпця — небезпечна операція.
    • ::warning — "Міграція — це append-only лог. НІКОЛИ не редагуйте вже застосовану міграцію."
  7. Підключення Flyway до Java-проекту (~100 рядків)
    • Maven/Gradle залежність.
    • Конфігурація: URL, user, password.
    • Програмний запуск: Flyway.configure().dataSource(...).load().migrate().
  8. Практичні завдання (~80 рядків)
  9. Підсумок (~30 рядків)

Стаття 7. Об'єктно-реляційний розрив (Impedance Mismatch)

Файл: content/04.java/pr2/07.impedance-mismatch.md
Обсяг: ~1000–1200 рядків

Зміст

  1. Вступ: Два світи, що не хочуть дружити (~100 рядків)
    • Hook: "У реляційній базі аудіокнига — це рядок із цифровим author_id. У Java — це об'єкт Audiobook з полем Author author. Хтось повинен перекласти."
    • Термін Impedance Mismatch (невідповідність імпедансів) з електротехніки.
  2. П'ять ключових розбіжностей (~250 рядків)
    • Гранулярність: Один об'єкт = одна таблиця? А якщо Author.Name — це Value Object з firstName + lastName?
    • Успадкування: ООП має ієрархії (PremiumUser extends User), реляційна модель — ні. Стратегії: Single Table, Table Per Class, Joined.
    • Ідентичність: == vs .equals() vs PK.
    • Зв'язки: Об'єкти мають посилання (audiobook.getAuthor()), таблиці — FK (число 550e8400...).
    • Навігація: ООП — крапка: user.getCollections().get(0).getAudiobooks(). SQL — JOIN. N+1 проблема.
    • ::card-group — картка для кожної розбіжності.
    • ::mermaid — паралельне порівняння: Java-об'єкти зліва ↔ SQL-таблиці справа.
  3. Як це впливає на код: Ручний маппінг (~200 рядків)
    • ResultSet → Java Object: рядок за рядком, rs.getString("first_name").
    • Java Object → PreparedStatement: параметр за параметром.
    • Маппінг зв'язків: зчитати author_id, зробити ще один SELECT, створити об'єкт.
    • Повний приклад: маппінг Audiobook з Author всередині.
    • ::code-group — маппінг "вручну" vs "з ORM" (без деталей ORM, лише щоб показати біль).
  4. Патерни подолання розриву (~150 рядків)
    • Active Record: об'єкт сам себе зберігає (простий, але порушує SRP).
    • Data Mapper: зовнішній маппер (що ми вже знаємо з 01.data-mapper-part1.md).
    • ORM (Object-Relational Mapping): автоматизація маппінгу (Hibernate, JPA) — тизер.
    • ::tabs — порівняння трьох підходів.
    • ::note — "Data Mapper — це ручний ORM. Розуміння його роботи — ключ до розуміння Hibernate."
  5. Зв'язок із Data Mapper: Підготовка до наступного модуля (~100 рядків)
    • Як саме наші Java-класи Author, Audiobook, Genre з'являться з таблиць, які ми спроектували.
    • Перехідний міст до 01.data-mapper-part1.md.
  6. Практичні завдання (~80 рядків)
  7. Підсумок (~30 рядків)

Стаття 8. Альтернативний погляд: А що, якби це була не реляційна БД?

Файл: content/04.java/pr2/08.beyond-relational.md
Обсяг: ~800–1000 рядків (коротша, оглядова)

Зміст

  1. Вступ: Реляційна модель — не єдиний шлях (~80 рядків)
    • Чому ми почали з агностичної концептуальної моделі: щоб показати, що одну доменну модель можна реалізувати по-різному.
  2. Документо-орієнтований підхід (MongoDB) (~200 рядків)
    • Як виглядала б аудіокнига як вбудований документ: автор та жанр вкладені в audiobook.
    • Embedding vs Referencing: коли вбудовувати, коли посилатися.
    • ::code-group — JSON документ MongoDB vs SQL-рядки для тієї ж сутності.
    • ::warning — "Вбудовування автора в кожну аудіокнигу = денормалізація за замовчуванням."
  3. Графовий підхід (Neo4j) (~150 рядків)
    • Вузли (Nodes): Author, Audiobook, User.
    • Ребра (Edges): WROTE, LISTENS_TO, ADDED_TO_COLLECTION.
    • Cypher-запит: MATCH (u:User)-[:LISTENS_TO]->(a:Audiobook)<-[:WROTE]-(auth:Author) RETURN auth.name.
    • ::note — "Помітьте: наша концептуальна модель (сутності + зв'язки) природно відображається в графову."
  4. Key-Value підхід (Redis) (~100 рядків)
    • Коли це доцільно: кешування сесій, лічильники, pub/sub.
    • listening_progress як ідеальний кандидат для Redis (гарячі дані з частим оновленням).
  5. Порівняльна таблиця: SQL vs Document vs Graph vs KV (~100 рядків)
    • Коли що обирати? Сценарії.
    • ::card-group або сравнительная таблиця.
  6. Polyglot Persistence: Найкращий із світів (~100 рядків)
    • Ідея: PostgreSQL для core data + Redis для кеша + Elasticsearch для full-text search.
    • ::mermaid — архітектура Polyglot Persistence для аудіоплатформи.
  7. Практичні завдання (~80 рядків)
  8. Підсумок (~50 рядків)

Навігаційний файл

NEW .navigation.yml в content/04.java/pr2/

title: Проектування баз даних

Рішення (затверджено)

  • Нумерація: 01., 02. ... всередині pr2/ (не залежно від 04.java).
  • Стаття 8 (Beyond Relational): Залишаємо (~800 рядків, оглядова).
  • DDL діалект: H2 як основний, з нотатками для PostgreSQL.

Verification Plan

Під час написання

  • Після кожного блоку (~100 рядків) — перевірка цілісності тексту, відповідності prompt.md.
  • Перевірка всіх Docus-компонентів: ::mermaid, ::plant-uml, ::tabs, ::steps, ::code-group, ::card-group.
  • Перевірка SQL-прикладів на відповідність реальній схемі з temp/pr2/sql/.

Після завершення модуля

  • Запуск dev-сервера (npm run dev) та перевірка рендерингу кожної статті.
  • Перевірка PlantUML-діаграм через PlantUML сервер.
  • Наскрізне читання: чи є логічний перехід від статті 1 до статті 8 та далі до 01.data-mapper-part1.md.