awesome-everything EN
↑ Обратно к восхождению

Базы данных

Реляционная модель: спроектировать и мигрировать multi-tenant схему

Суть Спроектируйте, укрепите и мигрируйте multi-tenant схему маркетплейса — keys, constraints, нормализация, выбор JSONB-vs-side-таблица, изоляция через RLS и смена колонки без простоя, доказанная от начала до конца.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на senior-высоте — в орбите
◷ 220 min

Читать про дизайн схемы — не то же, что жить со схемой два года. Спроектируйте реальную multi-tenant схему маркетплейса, обоснуйте каждый выбор key и constraint, изолируйте арендаторов через RLS, затем докажите, что можете менять её под нагрузкой, не сломав ни одного потребителя — это и есть цикл, который вы реально запускаете на продакшн-базе.

Цель

Превратите юнит в один инженерный артефакт: схему Postgres, где каждое решение по key, типу, constraint и хранению обосновано паттерном запроса, арендаторы изолированы вглубь, а ломающее изменение выкатывается без простоя — каждое утверждение подкреплено миграцией, которую можно запустить.

Проект
0 из 8
Цель

Спроектируйте multi-tenant B2B-схему маркетплейса в Postgres (workspaces, users, products, orders, order_items, tags), где каждый key, тип и constraint обоснован паттерном запроса; обеспечьте изоляцию арендаторов через Row-Level Security; затем выполните ломающее изменение без простоя через expand-then-contract — всё как запускаемые файлы миграций.

Требования
Критерии приёмки
  • schema.sql плюс упорядоченные файлы миграций, которые чисто применяются на пустом Postgres и воссоздают полную схему, включая все constraints, индексы, RLS-политику и миграцию expand-then-contract.
  • Одностраничное обоснование дизайна, связывающее каждое неочевидное решение (каждый surrogate key, каждый выбор FK ON DELETE, выбор JSONB-vs-side-таблица для тегов, снимок unit price) с паттерном запроса или режимом отказа, который оно защищает.
  • Вывод EXPLAIN ANALYZE для трёх горячих запросов, демонстрирующий index scan на FK- и tag-колонках при ~1M строк — снятый, а не предполагаемый — плюс демонстрация, что RLS возвращает только строки текущего workspace при заданном app.current_workspace_id.
  • Доказательство, что миграция expand-then-contract выполняется на заполненной таблице с parity-запросом, возвращающим ноль расхождений, и без поломки читателя во время transition (старая колонка читаема до шага contract).
Senior-стретч
  • Добавьте guard переходов состояния через BEFORE UPDATE триггер, отклоняющий нелегальные смены status заказа (например, completed не может вернуться в pending), и тест, что триггер отвергает плохой переход.
  • Добавьте сохранённый агрегат: колонку workspace_revenue_cents, поддерживаемую триггером на order_items, с reconciliation-запросом, который пере-выводит её из исходных строк и доказывает нулевой дрифт.
  • Добавьте DEFERRABLE INITIALLY DEFERRED foreign key для поддержки настоящего циркулярного или многошагового insert (например, самоссылающееся дерево категорий) и задокументируйте, почему immediate-проверка заблокировала бы его.
  • Сбенчмаркайте BIGSERIAL vs UUIDv4 vs UUIDv7 как primary key на крупнейшей таблице при ~10M строк и сообщите размер индекса и throughput вставок, подтвердив историю про locality из урока про heap и TOAST.
Итог

Это цикл, который вы запускаете на каждой реальной схеме: проектируйте под запросы и жизненный цикл, а не под первый insert; кладите identity в неизменяемый surrogate key и охраняйте business keys через UNIQUE NOT NULL; нормализуйте до 3NF и денормализуйте только с явным согласованием; выбирайте JSONB, типизированную колонку или side-таблицу по тому, как запрашиваете значение; делайте referential integrity и изоляцию арендаторов работой движка, а не приложения; и докажите, что можете менять схему под нагрузкой через expand-then-contract. Сделав это однажды на схеме маркетплейса, вы превращаете продакшн-версию в суждение, а не в угадывание.

Продолжить восхождение ↑Что такое индекс и как он ускоряет запросы
хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.