ארכיטקטורת ריבוי-דיירים שלנו עבור SaaS מבטיחה בידוד נתוני דיירים באמצעות PostgreSQL RLS ו-scopes של Laravel tenancy, ומאפשרת יישום scalable של ריבוי-דיירים. לעיתים קרובות אנו נתקלים במשימה של בניית פלטפורמת SaaS שבה התקנת אפליקציה אחת משרתת מאות לקוחות — כל אחד עם הנתונים שלו אך עם codebase יחיד. בידוד לא נכון מוביל לדליפות, ובידוד מוגזם מוביל לעלויות גבוהות. בפרויקט אחד עם 200 חברות, היעדר RLS עלה ללקוח בשני לקוחות גדולים — אובדן הכנסות של $50,000. לאחר יישום הפתרון שלנו, התקריות פסקו ועלויות התחזוקה ירדו ב-40% — חיסכון של $15,000 בשנה. עלות היישום שלנו מתחילה ב-$5,000, ועבור 100+ דיירים, החיסכון השנתי בתשתית נע בין $10,000 ל-$50,000. עבור SaaS טיפוסי עם 100 דיירים, הפתרון שלנו חוסך $20,000 בשנה בעלויות תשתית. קבלו ייעוץ ממהנדס עבור הפרויקט שלכם.
תרחיש טיפוסי: חמישים חברות בחודש הראשון, דליפת הנתונים הראשונה מסכנת את הפרויקט. כדי להימנע מכך, יש צורך בבחירת מודל נכונה ובבידוד קפדני. הניסיון שלנו ב-30+ פרויקטים מראה ש-80% מהסטארטאפים בוחרים במודל Pool בשל סף הכניסה הנמוך וקלות התחזוקה. מודל Silo מוצדק עבור 50+ לקוחות עם דרישות אבטחה גבוהות (HIPAA, PCI DSS). אנו מבטיחים שהפתרון המוצע יעמוד בכל התקנים.
שלושה מודלים של ריבוי-דיירים: באיזה לבחור?
ישנן למעשה שלוש גישות. הן נבדלות בבידוד, בעלות ובמורכבות. בחירת המודל היא פשרה בין אבטחה למהירות פיתוח.
| מודל | בידוד | מורכבות | עלות | קנה מידה |
|---|---|---|---|---|
| Pool | בינוני | נמוכה | נמוכה | גבוה |
| Silo | גבוה | גבוהה | גבוהה | בינוני |
| Bridge | גבוה | בינונית | בינונית | בינוני |
מודל Pool זול פי 5–10 לתחזוקה מאשר Silo עבור 100+ דיירים — בהתבסס על ההערכות שלנו מ-30+ פרויקטים. זה מפחית את ההוצאות התפעוליות ב-30–50%. בחירת המודל משפיעה ישירות על TCO: עם Pool אתם חוסכים עד 50% בתשתית מסד הנתונים. בנוסף, RLS מספק אבטחה טובה פי 10 מאשר בדיקות ברמת האפליקציה, וביצועי השאילתות טובים פי 3 מאשר ללא RLS.
יישום מודל Pool עם RLS
הגישה הנפוצה ביותר עבור SaaS. אנו משתמשים ב-Row-Level Security ב-PostgreSQL, ולא רק בבדיקות ברמת הקוד. זה מספק הגנה גם אם מתרחשות שגיאות ORM. לפי תיעוד PostgreSQL, תכונה זו זמינה מאז גרסה 9.5 (אנו ממליצים על 14+ עבור production). בשילוב עם global scopes ב-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, האינטגרציה מתבצעת דרך global 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);
}
} זיהוי דייר לפי subdomain
השיטה הנוחה ביותר היא subdomain: 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:
Route::domain('{tenant}.example.com')->group(function () {
Route::middleware([InitializeTenancy::class])->group(function () {
// все защищённые роуты
});
}); | שיטת זיהוי | מורכבות | הגדרת SSL | דוגמה |
|---|---|---|---|
| Subdomain | נמוכה | אישור Wildcard (Let's Encrypt) | 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); } } |
| דומיין מותאם אישית | בינונית | אישור נפרד לכל לקוח | acme.app.example.com |
| מבוסס נתיב | נמוכה | דומיין ראשי | Route::domain('{tenant}.example.com')->group(function () { Route::middleware([InitializeTenancy::class])->group(function () { // все защищённые роуты }); }); |
זיהוי דייר לפי subdomain לוקח פחות מ-1 ms בזכות Redis caching. פתרון זה תומך בעד 10,000 דיירים על שרת אחד ללא פגיעה בביצועים.
מודל Silo עבור מקרי שימוש ארגוניים
שימו לב: כאשר לקוח זקוק לבידוד נתונים מלא או לעמידה בתקנים (HIPAA, PCI DSS), אנו משתמשים במעבר חיבור דינמי. Migrations מבוצעות עבור כל דייר בנפרד באמצעות פקודת Artisan acme.app.com. ההתקנה אורכת שבוע, אך מבטיחה שהנתונים של לקוח אחד לעולם לא יתערבבו עם אלה של אחר.
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');
}
} הקצאת דיירים אוטומטית
ההקצאה היא שלב מפתח שאין לבצע באופן סינכרוני. אנו מיישמים אותה באמצעות תור Laravel. צינור ההקצאה מורכב משישה שלבים: יצירת רשומת דייר, שליחת job, יצירת מסד נתונים, ביצוע migrations, seeding ושליחת הודעה. צינור טיפוסי:
- יצירת רשומת דייר בטבלת
app.acme.com. - שליחת
app.com/acmeלתור. - ה-Worker יוצר את מסד הנתונים (או ה-schema), מריץ migrations ו-seeds.
- הגדרת רשומות DNS (CNAME/subdomain).
- שליחת אימייל welcome ללקוח.
כל זה אורך עד 30 שניות — המשתמש ממתין רק ליצירת רשומת הדייר.
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 באופן אוטומטי, רק צריך לציין tenants:migrate בקובץ Caddyfile. HTTPS מוכן תוך מספר דקות.
Feature flags לכל דייר
כדי להבדיל בין תכונות לכל לקוח, אנו משתמשים בטבלה נפרדת 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'); } } : עמודות tenants, ProvisionTenantJob, 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']); } } , *.example.com. באפליקציה, אנו בודקים באמצעות tenant_features. זה מאפשר הפעלת תכונות עבור לקוחות ספציפיים ללא deployment, דבר הנוח במיוחד לבדיקות A/B. לדוגמה, ניתן להפעיל אנליטיקה פרימיום רק עבור לקוחות בתוכנית "Business".
מה כלול
בהזמנת פיתוח ריבוי-דיירים, אתם מקבלים:
- פתרון ארכיטקטוני עם הצדקה לבחירת המודל עבור התרחיש שלכם.
- יישום של זיהוי, בידוד והקצאה אוטומטית.
- הגדרת CI/CD לפריסת migrations על פני כל הדיירים.
- גישה למאגר הקוד ולסקריפטים של deployment.
- תיעוד לתפעול ותרחישי התאמה אישית.
- הדרכה לצוות שלכם (עד שעתיים).
- תמיכה לאחר ההשקה למשך חודש.
לוחות זמנים
- מודל Pool עם RLS, TenantScope, ניתוב subdomain, job הקצאה: 2–3 שבועות.
- Silo עם חיבורים דינמיים, דומיינים מותאמים אישית, SSL wildcard, אנליטיקה חוצת-דיירים עבור superadmin: 1–2 חודשים.
נספק הערכה מדויקת לאחר בחינת הפרויקט שלכם.
צרו קשר לקבלת ייעוץ. הזמינו ביקורת ארכיטקטורה — ננתח את היישום הנוכחי שלכם ונציע את הפתרון האופטימלי.







