Какие основные таблицы ты создаешь при проектировании БД для типового веб-приложения?

Ответ

Структура зависит от домена, но для многих веб-приложений (например, SaaS, маркетплейс, блог) я начинаю с набора базовых таблиц, которые обеспечивают фундамент:

  1. users — центральная таблица пользователей.

    CREATE TABLE users (
        id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
        email VARCHAR(255) UNIQUE NOT NULL,
        password_hash VARCHAR(255) NOT NULL, -- или ссылка на auth provider
        name VARCHAR(100),
        email_verified_at TIMESTAMP NULL,
        is_active BOOLEAN DEFAULT TRUE,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
    );
  2. roles & permissions — для управления доступом (RBAC). Часто используется связь многие-ко-многим через user_roles и role_permissions.

  3. Основные сущности предметной области. Например, для CMS:

    • posts (id, title, slug, content, author_id, status, published_at)
    • categories (id, name, slug)
    • post_category (post_id, category_id) — связующая таблица.
  4. Таблицы для связей многие-ко-многим. Например, user_likes (user_id, post_id, created_at) для лайков.

  5. audit_logs или activity_logs — для отслеживания важных действий (кто, что, когда сделал).

    CREATE TABLE audit_logs (
        id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
        user_id BIGINT UNSIGNED NULL, -- NULL если действие совершено неавторизованно/системой
        action VARCHAR(50) NOT NULL, -- e.g., 'user.login', 'order.created'
        entity_type VARCHAR(50) NULL, -- e.g., 'Order', 'User'
        entity_id BIGINT UNSIGNED NULL,
        old_values JSON NULL,
        new_values JSON NULL,
        ip_address VARCHAR(45),
        user_agent TEXT,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL
    );

Ключевые практики, которые я применяю сразу:

  • Использование BIGINT для первичных ключей.
  • Внешние ключи (FOREIGN KEY) для обеспечения ссылочной целостности.
  • Индексы на поля, используемые в WHERE, JOIN, ORDER BY (например, email, user_id, created_at).
  • TIMESTAMP с автоматическим обновлением для created_at/updated_at.
  • Мягкое удаление (deleted_at TIMESTAMP NULL) вместо физического DELETE.

Ответ 18+ 🔞

А, ну ты про структуру базы спрашиваешь? Давай разберём, как это у нормальных людей делается, а не как у этих... мартышлюшек, которые потом три года фиксы пилят.

Смотри, основа основ — это таблица users. Без неё нихуя не работает. Ты ж не хочешь, чтобы пользователи как бомжи без прописки по твоему приложению шатались?

CREATE TABLE users (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    email VARCHAR(255) UNIQUE NOT NULL,
    password_hash VARCHAR(255) NOT NULL, -- или ссылка на auth provider
    name VARCHAR(100),
    email_verified_at TIMESTAMP NULL,
    is_active BOOLEAN DEFAULT TRUE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

Вот это — база, ёпта. BIGINT для id — потому что ты же не хочешь через год охуеть, когда у тебя юзеров будет овердохуища, а ключи уже кончатся? А email_verified_at — это чтобы знать, кто реальный чувак, а кто просто спам-бот, который тебе всю статистику испортит.

Дальше — роли и разрешения. Это, бля, обязательно. Иначе получится бардак, как в коммуналке: все всем всё могут, а потом один пидарас шерстяной всё похерит. Делаешь roles, permissions, и связки между ними. RBAC, ёб твою мать, стандарт де-факто.

Потом — твои бизнес-сущности. Допустим, делаешь блог. Ну ясное дело: posts (статьи), categories (категории), и связующая таблица post_category, потому что одна статья может быть в нескольких категориях. Это ж логично, да? Не делать же в посте десять полей category_id_1, category_id_2... Это же пиздопроебибна какая-то.

А вот это, слушай, очень важная штука — audit_logs. Ты думаешь, можно без неё? А потом придёт к тебе начальник и спросит: «А кто, сука, эту запись в три часа ночи удалил?». А ты стоишь, бздишь, и нихуя не знаешь.

CREATE TABLE audit_logs (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    user_id BIGINT UNSIGNED NULL, -- NULL если действие совершено неавторизованно/системой
    action VARCHAR(50) NOT NULL, -- e.g., 'user.login', 'order.created'
    entity_type VARCHAR(50) NULL, -- e.g., 'Order', 'User'
    entity_id BIGINT UNSIGNED NULL,
    old_values JSON NULL,
    new_values JSON NULL,
    ip_address VARCHAR(45),
    user_agent TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL
);

Видишь? old_values и new_values в JSON. Теперь ты всегда сможешь посмотреть, что было и что стало. Это не паранойя, это профессиональная щепетильность, чувак. Доверия ебать ноль — особенно если у тебя команда больше двух человек.

И ещё несколько правил, которые надо в подкорку вбить:

  • Внешние ключи (FOREIGN KEY) — это не для красоты. Это чтобы в базе не появились записи-сироты, которые ссылаются на хуй с горы. Целостность данных, мать её.
  • Индексы — ставь на всё, что участвует в WHERE, JOIN или ORDER BY. Без индексов твои запросы будут тормозить так, что хоть на кофе ходи. Представь, что ищешь юзера по email без индекса — это как в тёмной комнате на ощупь искать чёрную кошку.
  • Мягкое удаление (deleted_at) — вместо физического DELETE. Удалил запись, а она просто помечена. Потом если клиент придёт с воплями «верните мои данные!», ты не будешь как полупидор объяснять, что всё накрылось медным тазом. Восстановил и всё.

Вот примерно так. Сначала фундамент, потом стены, потом крыша. А не наоборот, а то получится хитрая жопа, а не архитектура.