Три основні моделі, від найпростішої до найізольованішої.
1. Спільні таблиці з колонкою tenant_id:
CREATE TABLE projects (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
tenant_id bigint NOT NULL REFERENCES tenants (id),
name text NOT NULL,
UNIQUE (tenant_id, name)
);
CREATE INDEX projects_tenant_idx ON projects (tenant_id);
- Плюси: одна схема, прості міграції, ефективне використання ресурсів, легко рахувати аналітику по всіх тенантах.
- Мінуси: головний ризик - забутий фільтр
WHERE tenant_id = ?, і один клієнт бачить дані іншого. Захист - глобальні скоупи в застосунку, Row-Level Security у PostgreSQL, тести на ізоляцію. «Галасливий» тенант навантажує всіх.
2. Схема на тенанта (tenant_42.projects):
- Плюси: ізоляція на рівні об'єктів бази - запит без фільтра не побачить чужих даних; легко видалити чи вивантажити дані одного клієнта.
- Мінуси: міграції треба застосовувати до сотень чи тисяч схем; тисячі схем × десятки таблиць - велике навантаження на системний каталог і на
pg_dump; пул з'єднань ускладнюється (search_pathна кожен запит).
3. Окрема база на тенанта:
- Плюси: максимальна ізоляція, окремі бекапи й відновлення, можна розмістити великого клієнта на окремому сервері, виконати вимоги щодо розташування даних.
- Мінуси: найдорожче в експлуатації: міграції, моніторинг і оновлення для кожної бази; крос-тенантна аналітика вимагає окремого сховища.
Як обирати:
- Багато невеликих клієнтів (SaaS для малого бізнесу) - спільні таблиці + RLS чи надійні глобальні скоупи.
- Помірна кількість клієнтів з вимогами ізоляції - схеми.
- Великі корпоративні клієнти з договірними вимогами - окремі бази (часто гібрид: більшість у спільних таблицях, найбільші - окремо).
Незалежно від моделі: tenant_id у всіх унікальних обмеженнях і індексах; тести, що перевіряють ізоляцію для кожної сутності; продумана поведінка фонових задач і кешу (ключі кешу з ідентифікатором тенанта).