коротко

Одну и ту же предметную область описывают на трёх уровнях детализации. Концептуальная модель — сущности и связи на языке бизнеса, без атрибутов и техники («есть Клиент, он делает Бронирования»). Логическая модель — добавляет атрибуты, ключи и нормализацию, но не привязана к конкретной СУБД. Физическая модель — это уже таблицы, типы данных и индексы в конкретной базе (PostgreSQL, MySQL). Одно перетекает в другое сверху вниз: бизнес-сущность → таблица с полями → DDL. А чтобы все три уровня говорили об одном и том же одними и теми же словами, нужен глоссарий (словарь данных) — единый список терминов. Аналитик чаще всего живёт на концептуальном и логическом уровнях, физику отдаёт разработчику.

Однажды я нарисовал красивую схему таблиц и принёс её на встречу с заказчиком — владельцем сети отелей. Через пять минут он спросил: «А почему у вас „гость“ и „плательщик“ — это одно и то же? У нас компания бронирует, а живёт сотрудник». Я завис. В моей схеме это была одна таблица users, потому что я думал про базу, а не про бизнес. Мы потеряли полчаса, разбирая то, что надо было проговорить на салфетке за пять минут — до всяких таблиц. С тех пор я не сажусь рисовать поля, пока не договорился с бизнесом о сущностях.

Это и есть главная мысль про уровни модели данных: они существуют не ради красоты методологии, а чтобы разговаривать с разными людьми на их языке. С заказчиком — про сущности и связи. С другим аналитиком — про атрибуты и ключи. С разработчиком — про типы и индексы. Если смешать уровни, разговор ломается: бизнес не понимает VARCHAR(255), а разработчик не может собрать схему из «ну, клиент как-то связан с бронированием».

Три уровня: бизнес, логика, железо

Представьте, что вы описываете один и тот же дом. Архитектору вы говорите «трёхэтажный дом с гаражом и садом» — это концепт. Проектировщику — «несущие стены тут, окна такого размера, проводка такая» — это логика. Строителю — «кирпич марки М150, арматура 12 мм, бетон В25» — это физика. Дом один, описаний три, и каждое нужно своему собеседнику.

УровеньЧто описываетПример (бронирование)
КонцептуальнаяСущности и связи на языке бизнеса. Без атрибутов, без техники. Отвечает на «что вообще есть и как связано».Есть Гость, есть Номер, есть Бронирование. Гость делает много Бронирований, каждое Бронирование — на один Номер.
ЛогическаяАтрибуты, первичные и внешние ключи, нормализация. Независима от СУБД. Отвечает на «какие поля и как они связаны ключами».Бронирование(id, guest_id, room_id, дата_заезда, дата_выезда, статус). guest_id ссылается на Гостя, room_id — на Номер.
ФизическаяТаблицы, типы данных, индексы в конкретной базе. Отвечает на «как это лежит в PostgreSQL».booking(id BIGSERIAL PK, guest_id BIGINT FK, room_id INT FK, check_in DATE, status VARCHAR(20), индекс по guest_id).

Заметьте, как растёт детализация и сужается аудитория. Концептуальную модель поймёт директор отеля. Логическую — любой аналитик и разработчик. Физическую — тот, кто реально пишет миграцию под конкретную базу. Чем ниже спускаемся, тем больше решений «как», и тем меньше людей, которым это надо видеть.

Как одно перетекает в другое

Уровни — это не три разных документа, которые живут сами по себе. Это один и тот же объект, который вы постепенно уточняете. Начинаете сверху: ловите сущности из разговора с бизнесом. Потом навешиваете на них атрибуты и ключи — получается логическая модель. Потом разработчик (или вы вместе с ним) превращает её в DDL под конкретную СУБД — это физика.

Возьмём нашу сущность «Бронирование» и проследим её через все три уровня.

Концептуальный уровень — словами: «Гость бронирует Номер на период. Одно бронирование — на один номер и одного гостя, но гость может бронировать много раз». Никаких полей, никаких типов. Только сущности и связи. Это то, что я рисую с заказчиком.

Логический уровень — добавляем атрибуты и ключи, но всё ещё без привязки к базе:

Бронирование
  id           — первичный ключ
  guest_id     — внешний ключ на Гостя
  room_id      — внешний ключ на Номер
  дата_заезда  — дата
  дата_выезда  — дата
  статус       — одно из: новое, оплачено, заселён, отменено
  сумма        — деньги

Физический уровень — то же самое, но уже для PostgreSQL: реальные типы, ограничения, индекс под частый поиск по гостю:

CREATE TABLE booking (
  id          BIGSERIAL PRIMARY KEY,
  guest_id    BIGINT NOT NULL REFERENCES guest(id),
  room_id     INT    NOT NULL REFERENCES room(id),
  check_in    DATE   NOT NULL,
  check_out   DATE   NOT NULL,
  status      VARCHAR(20) NOT NULL DEFAULT 'new',
  amount      NUMERIC(10,2) NOT NULL,
  CHECK (check_out > check_in)
);
CREATE INDEX idx_booking_guest ON booking(guest_id);

Один объект, три представления. На физическом уровне появились вещи, которых не было выше: NUMERIC(10,2) вместо абстрактных «денег», DEFAULT, CHECK на даты, индекс. Это решения уровня СУБД — и именно поэтому их не тащат в разговор с бизнесом. А как менять такую физическую схему на работающей базе, не уронив прод, — в записи про миграции данных. Подробнее про ключи, связи и саму ER-нотацию — в записи базы данных для аналитика: то, что там названо ER-моделью, — это как раз логический и физический уровни. Но всё это — про реляционную физику; в документных и других NoSQL-базах физический уровень моделируют иначе — под паттерн доступа, а не под нормализацию.

Как это выглядит на ER-диаграмме

erDiagram
  GUEST ||--o{ BOOKING : "делает"
  ROOM ||--o{ BOOKING : "забронирован в"
  GUEST {
    bigint id PK
    string full_name
    string phone
  }
  ROOM {
    int id PK
    string number
    int capacity
  }
  BOOKING {
    bigint id PK
    bigint guest_id FK
    int room_id FK
    date check_in
    date check_out
    string status
  }

Схема выше — логический уровень модели брони в нотации ER. Один Гость (GUEST) делает много Бронирований (BOOKING) — связь один-ко-многим, поэтому в брони лежит внешний ключ guest_id. Один Номер (ROOM) тоже фигурирует во многих бронированиях за свою жизнь — отсюда room_id. Концептуальный уровень — это та же картинка, но без блоков с атрибутами: только три прямоугольника и две линии между ними. Физический — та же картинка, но типы стали конкретными для PostgreSQL (bigint, varchar) и добавились индексы, которые на ER обычно не рисуют.

Глоссарий и словарь данных: единый язык

Теперь про то, без чего все три уровня рассыпаются. Помните мой провал с «гостем» и «плательщиком»? Это была не ошибка схемы — это была ошибка языка. У меня и у заказчика слово «клиент» значило разное, и пока мы это не проговорили, любая модель была неверной.

Глоссарий (он же словарь данных, data dictionary) — это документ, где каждый термин предметной области определён одним согласованным способом. Что такое «Гость»? Что такое «Заезд»? Чем «отменено» отличается от «не оплачено»? Глоссарий отвечает на эти вопросы один раз и для всех.

ТерминОпределение
ГостьФизлицо, которое фактически проживает в номере. Может не совпадать с плательщиком.
ПлательщикТот, кто оплачивает бронь: гость или компания. Отдельная сущность.
БронированиеЗаявка на проживание в конкретном номере на период. Имеет статус.
Заезд (check-in)Факт физического заселения гостя. Меняет статус брони на «заселён».

Глоссарий — это часть ТЗ, а не приложение к нему

Единый язык терминов экономит больше времени, чем любая красивая схема. Когда бизнес, аналитик и разработчик называют одну вещь одним словом — исчезает целый класс багов «мы имели в виду разное». Поэтому глоссарий живёт прямо в спецификации; как его туда встроить — в записи как написать ТЗ; готовый сводный глоссарий терминов справочника лежит отдельной записью. Правило простое: если термин может быть понят двояко — он идёт в глоссарий.

Доменная модель — где здесь она

Иногда рядом звучит слово «доменная модель» (domain model), и его путают с уровнями выше. Грубо: доменная модель — это концептуальная модель плюс правила поведения. Не только «Бронь связана с Гостем», но и «бронь нельзя отменить после заезда», «нельзя забронировать занятый номер». То есть доменная модель — это сущности + связи + бизнес-правила, выраженные на языке предметной области. Концептуальная модель данных — её статическая часть (что есть), а поведение (что можно делать) уже ближе к диаграммам состояний — про них в записи диаграммы состояний.

Не прыгайте сразу в физику

Самая частая ошибка джуна (моя в том числе) — открыть редактор и начать рисовать таблицы. Это соблазнительно: чувствуешь, что делаешь дело. Но если вы не договорились о сущностях и терминах на концептуальном уровне, вы рисуете красивую схему неправильной системы. Сначала сущности и глоссарий, потом атрибуты и ключи, и только потом типы и индексы.

Откуда это взялось

Идею разделять описание данных на уровни оформил комитет ANSI/SPARC в 1975 году — они предложили трёхуровневую архитектуру (внешний, концептуальный, внутренний уровни), чтобы отделить «что данные значат» от «как они хранятся». Годом позже, в 1976-м, Питер Чен опубликовал статью про ER-модель — способ рисовать сущности и связи независимо от того, в какой базе они потом окажутся. Это и дало аналитикам язык для концептуального и логического уровней, который пережил смену поколений СУБД. Тому, чем вы пользуетесь каждый день, — полвека, и это признак, что идея верная.

Как это спрашивают на собесе

«Чем концептуальная модель отличается от логической и физической?» — отвечайте через детализацию и аудиторию: концептуальная — сущности и связи для бизнеса, логическая — атрибуты и ключи без привязки к СУБД, физическая — таблицы, типы, индексы в конкретной базе. Хороший ход — сразу провести один пример через все три уровня (бронь, заказ — что угодно). Подвох-вопрос: «А зачем вообще три уровня, почему не сразу таблицы?» — отвечайте, что разные уровни нужны разным собеседникам и что без концептуального уровня легко спроектировать не ту систему. Если спросят про глоссарий — это про единый язык терминов, чтобы все понимали слова одинаково.

Частые вопросы

Аналитик должен делать все три уровня?

Обычно аналитик отвечает за концептуальный и логический уровни: договаривается о сущностях с бизнесом и описывает атрибуты, ключи, связи. Физический уровень — типы данных, индексы, конкретный DDL под СУБД — чаще делает разработчик, иногда вместе с аналитиком. Но понимать все три надо: без этого вы не проверите, что логическая модель вообще ляжет в базу.

Чем логическая модель отличается от физической, если поля те же?

Логическая модель не привязана к конкретной СУБД: там «дата», «деньги», «строка», ключи и связи. Физическая — это уже PostgreSQL или MySQL: DATE, NUMERIC(10,2), VARCHAR(255), индексы, ограничения CHECK, стратегия партиционирования. Логическая отвечает на «что и как связано», физическая — «как именно это лежит в этой базе». Одну логическую модель можно реализовать в разных СУБД по-разному.

Зачем нужен глоссарий, если есть модель данных?

Модель показывает структуру, но не объясняет, что термины значат на языке бизнеса. «Клиент», «гость», «плательщик» могут оказаться разными сущностями — или одной, в зависимости от предметной области, и это надо зафиксировать словами. Глоссарий (словарь данных) даёт единый язык: один термин — одно согласованное определение для бизнеса, аналитика и разработчика. Он убирает класс багов «мы имели в виду разное» и живёт прямо в ТЗ.