~/VibeHandbook
PDF бесплатно

Глава 14 · 03

Основы проектирования схемы

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

  • Одна таблица на «сущность». Пользователи, посты, заказы — каждое получает свою таблицу.
  • Каждой строке нужен уникальный ID (первичный ключ).
  • Связывайте таблицы внешними ключами, а не копированием данных туда-сюда.
  • Выбирайте честные типы. Деньги — это decimal, а не float. Даты — это временные метки, а не текст.
  • Не дублируйте. Если email пользователя живёт в пяти таблицах, в конце концов у вас будет пять разных email.

Вот простая схема для блога — такая, какую AI сгенерирует для вас:

CREATE TABLE users (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    email       TEXT NOT NULL UNIQUE,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE posts (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    author_id   UUID NOT NULL REFERENCES users(id),
    title       TEXT NOT NULL,
    body        TEXT NOT NULL,
    published   BOOLEAN NOT NULL DEFAULT false,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE INDEX idx_posts_author ON posts(author_id);

Строка author_id ... REFERENCES users(id) — это и есть внешний ключ. Он привязывает каждый пост ровно к одному реальному пользователю, так что сама база данных откажется хранить пост без действительного автора:

   users                          posts
   ┌─────────────┐                ┌─────────────┐
   │ id  (PK) ◀──┼────────────────┤ author_id   │  (FK → users.id)
   │ email       │ у одного юзера  │ id  (PK)    │
   │ created_at  │ много постов    │ title       │
   └─────────────┘                 │ body        │
                                   │ published   │
        1  ────────────────────▶  *└─────────────┘
       (один)                   (много)

Обратите внимание на REFERENCES (внешний ключ, привязывающий пост к реальному пользователю) и ограничения NOT NULL и UNIQUE. Это страховочные барьеры, которые база данных обеспечивает за вас, так что плохие данные не смогут проскользнуть, даже когда в вашем коде есть баг.

Ограничение, которое стоит добавлять осознанно, — это что происходит, когда родитель исчезает. По умолчанию удаление пользователя, на которого всё ещё ссылаются его посты, завершится ошибкой — что часто и есть то, чего вы хотите. Но можно задать явно: REFERENCES users(id) ON DELETE CASCADE удалит и посты тоже, а ON DELETE RESTRICT заблокирует удаление. Выбрать намеренно лучше, чем узнать поведение по умолчанию болезненным путём в продакшене. Тот же инстинкт «пусть база данных защитит вас» применим к ограничению CHECK (CHECK (price >= 0)) — оно отвергает бессмысленные данные у источника, вместо того чтобы доверять валидации в каждом пути кода.

Хотите офлайн-версию?

Скачайте всю книгу в PDF или EPUB — бесплатно.