Мультиорендність (multi-tenancy) - один застосунок обслуговує багато клієнтів-орендарів (компанії, школи, магазини), і дані кожного мають бути ізольовані від інших.
Три основні моделі зберігання:
1. Спільна база, колонка tenant_id:
#[ScopedBy(TenantScope::class)]
class Project extends Model {}
final class TenantScope implements Scope
{
public function apply(Builder $builder, Model $model): void
{
$builder->where('tenant_id', app(CurrentTenant::class)->id);
}
}
- плюси: просто, дешево, одна міграція на всіх, легко робити звіти по всіх орендарях;
- мінуси: ізоляція тримається на коді - один забутий фільтр (сирий запит,
withoutGlobalScopes, джоба без контексту орендаря) - і дані одного клієнта бачить інший. «Галасливий сусід» навантажує базу для всіх.
Посилення: Row-Level Security у PostgreSQL - політика на рівні бази (USING (tenant_id = current_setting('app.tenant_id')::int)): навіть помилка в застосунку не поверне чужих рядків. Застосунок встановлює змінну сесії бази на кожен запит.
2. Окрема схема на орендаря (PostgreSQL schemas): ізоляція сильніша, бекап і відновлення окремого клієнта простіші, але міграції треба проганяти по всіх схемах, а тисячі схем ускладнюють обслуговування.
3. Окрема база на орендаря: найсильніша ізоляція, окремі ресурси, можливість розмістити великого клієнта на окремому сервері чи в іншому регіоні (вимоги до зберігання даних). Ціна - складна інфраструктура, міграції по сотнях баз, з'єднання, крос-орендна аналітика.
Що треба вирішити незалежно від моделі:
- визначення поточного орендаря: піддомен (
acme.app.com), домен, шлях, обраний у сесії - і перевірка, що користувач належить орендарю; - контекст у фонових процесах: джоби, команди, планувальник не мають запиту - ідентифікатор орендаря передається явно в задачу й відновлюється перед виконанням;
- кеш, файли, черги, пошук - ключі й шляхи з префіксом орендаря, інакше витік через кеш;
- унікальність -
unique(['tenant_id', 'email']), а не глобальна; - тести ізоляції: для кожного ресурсу - «орендар A не бачить даних орендаря B».
Як обрати: більшість SaaS починає зі спільної бази з tenant_id (+ RLS для критичних даних) і переходить до окремих баз лише для великих клієнтів чи регуляторних вимог. Пакети stancl/tenancy і spatie/laravel-multitenancy реалізують обидва підходи.
Докладніше в документації: PostgreSQL: політики безпеки рядків