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

«Какие основные таблицы ты создаешь при проектировании БД для типового веб-приложения?» — вопрос из категории Базы данных, который задают на 24% собеседований PHP Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Структура зависит от домена, но для многих веб-приложений (например, 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.