Ответ
Структура зависит от домена, но для многих веб-приложений (например, SaaS, маркетплейс, блог) я начинаю с набора базовых таблиц, которые обеспечивают фундамент:
-
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 ); -
roles&permissions— для управления доступом (RBAC). Часто используется связь многие-ко-многим черезuser_rolesиrole_permissions. -
Основные сущности предметной области. Например, для CMS:
posts(id, title, slug, content, author_id, status, published_at)categories(id, name, slug)post_category(post_id, category_id) — связующая таблица.
-
Таблицы для связей многие-ко-многим. Например,
user_likes(user_id, post_id, created_at) для лайков. -
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_atTIMESTAMP 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. Удалил запись, а она просто помечена. Потом если клиент придёт с воплями «верните мои данные!», ты не будешь как полупидор объяснять, что всё накрылось медным тазом. Восстановил и всё.
Вот примерно так. Сначала фундамент, потом стены, потом крыша. А не наоборот, а то получится хитрая жопа, а не архитектура.