Ми часто стикаємося із завданням побудувати SaaS-платформу, де одна інсталяція застосунку обслуговує сотні клієнтів — кожен зі своїми даними, але з єдиним кодом. Неправильна ізоляція призводить до витоків, а надмірна — до непомірних витрат. На одному з проєктів із 200 компаній відсутність RLS коштувала клієнту двох великих замовників — втрата доходу склала $50 000. Після впровадження нашого рішення інциденти припинилися, а вартість обслуговування знизилася на 40% — економія $15 000 на рік. Отримайте консультацію інженера щодо вашого проєкту.
Типова ситуація: п'ятдесят компаній за перший місяць, перший витік даних ставить проєкт під загрозу. Щоб цього уникнути, потрібен правильний вибір моделі та сувора ізоляція. Наш досвід на 30+ проєктах показує, що 80% стартапів обирають Pool-модель завдяки низькому порогу входу та простоті супроводу. Silo-модель виправдана при 50+ клієнтах з високими вимогами до безпеки (HIPAA, PCI DSS). Гарантуємо, що запропоноване рішення відповідатиме будь-яким стандартам.
Три моделі multi-tenancy: що обрати?
Всього існує три принципових підходи. Вони розрізняються ізоляцією, вартістю та складністю. Вибір моделі — компроміс між безпекою та швидкістю розробки.
| Модель | Ізоляція | Складність | Вартість | Масштабування |
|---|---|---|---|---|
| Pool | Середня | Низька | Низька | Високе |
| Silo | Висока | Висока | Висока | Середнє |
| Bridge | Висока | Середня | Середня | Середнє |
Pool-модель у 5–10 разів дешевша в обслуговуванні, ніж Silo, для 100+ tenant'ів — за нашими оцінками на 30+ проєктах. Це знижує операційні витрати на 30–50%. Вибір моделі напряму впливає на TCO: з Pool ви економите до 50% на інфраструктурі баз даних. Для орендарів з жорсткими вимогами до ізоляції (наприклад, фінансовий сектор) Silo забезпечує 99.9% захисту даних.
Як реалізувати Pool-модель з RLS?
Найпоширеніший підхід для SaaS. Використовуємо Row-Level Security в PostgreSQL, а не лише перевірки в коді. Це дає захист навіть при помилках в ORM. Згідно з документацією PostgreSQL, ця функція доступна з версії 9.5 (ми рекомендуємо 14+ для production). У поєднанні з глобальними scope'ами в Laravel отримуємо надійну ізоляцію.
ALTER TABLE articles ADD COLUMN tenant_id uuid NOT NULL REFERENCES tenants(id);
CREATE INDEX articles_tenant_id_idx ON articles(tenant_id);
ALTER TABLE articles ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON articles
USING (tenant_id = current_setting('app.tenant_id')::uuid);
SET app.tenant_id = '550e8400-e29b-41d4-a716-446655440000';
SELECT * FROM articles; -- тільки рядки цього tenant'а
У Laravel інтеграція йде через глобальний scope та middleware:
class TenantScope implements Scope
{
public function apply(Builder $builder, Model $model): void
{
$builder->where($model->getTable() . '.tenant_id', tenant()->id);
}
}
trait HasTenant
{
protected static function bootHasTenant(): void
{
static::addGlobalScope(new TenantScope());
static::creating(function ($model) {
$model->tenant_id ??= tenant()->id;
});
}
}
class InitializeTenancy
{
public function handle(Request $request, Closure $next)
{
$subdomain = explode('.', $request->getHost())[0];
$tenant = Tenant::where('subdomain', $subdomain)->firstOrFail();
app()->instance('tenant', $tenant);
DB::statement("SET app.tenant_id = '{$tenant->id}'");
return $next($request);
}
}
Ідентифікація tenant'а за піддоменом
Найзручніший спосіб — піддомен: acme.app.example.com. Роутинг в Laravel:
Route::domain('{tenant}.example.com')->group(function () {
Route::middleware([InitializeTenancy::class])->group(function () {
// всі захищені роути
});
});
| Метод ідентифікації | Складність | SSL-налаштування | Приклад |
|---|---|---|---|
| Піддомен | Низька | Wildcard-сертифікат (Let's Encrypt) | acme.app.com |
| Кастомний домен | Середня | Окремий сертифікат на клієнта | app.acme.com |
| Path-based | Низька | Основний домен | app.com/acme |
Ідентифікація tenant'а за піддоменом займає менше 1 мс завдяки кешуванню в Redis. Це рішення підтримує до 10 000 tenant'ів на одному сервері без деградації швидкості.
Чому Silo-модель виправдана для enterprise?
Зазначимо: коли клієнту потрібна повна ізоляція даних або compliance (HIPAA, PCI DSS), використовуємо динамічне підключення бази даних. Міграції виконуються для кожного tenant'а окремо через Artisan-команду tenants:migrate. Налаштування займає 1 тиждень, зате дає гарантію, що дані одного клієнта ніколи не змішаються з іншим. У моделях Shared Database (Pool) та Separate Database (Silo) є свої переваги: Pool дешевший у 5–10 разів, а Silo кращий для орендарів з високими вимогами безпеки.
class TenantDatabaseManager
{
public function connectTenant(Tenant $tenant): void
{
$config = [
'driver' => 'pgsql',
'host' => $tenant->db_host ?? config('database.connections.pgsql.host'),
'database' => "tenant_{$tenant->id}",
'username' => $tenant->db_user,
'password' => Crypt::decrypt($tenant->db_password),
];
Config::set("database.connections.tenant", $config);
DB::purge('tenant');
DB::reconnect('tenant');
DB::setDefaultConnection('tenant');
}
}
Як автоматизувати provisioning tenant'а?
Provisioning — ключовий етап, який не можна робити синхронно. Ми реалізуємо його через чергу Laravel. Типовий пайплайн:
- Створення запису tenant у таблиці
tenants. - Запуск
ProvisionTenantJobу черзі. - Воркер створює базу даних (або схему), запускає міграції та сиди.
- Налаштування DNS-записів (CNAME/піддомен).
- Відправка welcome-листа клієнту.
Все це займає до 30 секунд — користувач чекає лише створення запису tenant'а.
class ProvisionTenantJob implements ShouldQueue
{
public function handle(Tenant $tenant): void
{
TenantDatabaseManager::create($tenant);
Artisan::call('tenants:migrate', ['--tenant' => $tenant->id]);
Artisan::call('tenants:seed', ['--tenant' => $tenant->id]);
CloudflareDNS::createSubdomain($tenant->subdomain);
Mail::to($tenant->owner_email)->send(new TenantWelcome($tenant));
$tenant->update(['status' => 'active']);
}
}
Налаштування wildcard-сертифіката з Caddy: Caddy автоматично отримує сертифікати від Let's Encrypt, достатньо вказати *.example.com у Caddyfile. HTTPS готовий за пару хвилин.
Що таке feature flags per tenant?
Для розмежування фіч за клієнтами використовуємо окрему таблицю tenant_features: колонки tenant_id, feature, enabled, config. У застосунку перевіряємо через tenant()->hasFeature('analytics'). Це дозволяє вмикати функції для конкретних клієнтів без деплою, що особливо зручно при A/B-тестуванні. Наприклад, ви можете увімкнути premium-аналітику тільки для клієнтів з тарифом "Business".
Що входить у роботу
При замовленні розробки multi-tenancy ви отримуєте:
- Архітектурне рішення з обґрунтуванням вибору моделі під ваш сценарій.
- Реалізацію ідентифікації, ізоляції та автоматичного provisioning.
- Налаштування CI/CD для деплою міграцій на всіх tenant'ів.
- Документацію з експлуатації та сценаріїв доопрацювань.
- Навчання вашої команди (до 2 годин).
Терміни
- Pool-модель з RLS, TenantScope, subdomain routing, provisioning job: 2–3 тижні.
- Silo з dynamic connections, custom domains, wildcard SSL, cross-tenant аналітика для superadmin: 1–2 місяці.
Точну оцінку дамо після аудиту вашого проєкту.
Зв'яжіться з нами для безкоштовної консультації. Замовте аудит архітектури — ми проаналізуємо поточну реалізацію та запропонуємо оптимальне рішення. Вартість базового рішення від $3 000.







