🏗️אדריכלות backend

Multi-tenant SaaS על .NET 10

שלוש רמות בידוד - DB משותף, סכמה נפרדת, cluster נפרד. טריגרי מעבר וחוזה ITenantScoped שבלעדיו הכל נשבר.

מאת: אמיל סלאבין · 11 ביוני 2026 · 15 דקות קריאה

TL;DR

Multi-tenant ≠ tenant_id ב-WHERE. בידוד, רגולציה, latency, observability - הכל משתנה בכל רמה.

3
רמות בידוד
15 דק'
קריאה
.NET 10
production
200+
tenants/cluster

שלוש רמות בידוד

L1: DB משותף + עמודת tenant_id

זול, מהיר להתחיל. כל הטבלאות עם TenantId. EF Core 10 query filter מוסיף WHERE tenant_id = X אוטומטית. מתאים ל-startup וכן ל-B2B עם tenants הומוגניים. סכנה: filter שכוח אחד - דליפה. חוזה ITenantScoped + autotests חובה.

L2: סכמה (search_path) נפרדת לכל tenant

כל tenant - סכמת Postgres משלו. Connection string משותף, אבל middleware מגדיר search_path לפי tenant ב-JWT. בידוד ברמת DDL - אי אפשר לקרוא נתונים זרים בטעות. מתאים לרגולטוריקה. המחיר: migrations × N, monitoring קשה יותר, backup per tenant.

L3: cluster נפרד לכל tenant

כל enterprise tenant - namespace נפרד ב-Kubernetes, DB משלו. בידוד מלא, dedicated SLA, אפשר ב-region של data residency. יקר - רק לתוכניות enterprise.

טריגרי מעבר L1 - L2 - L3

  • L1 - L2: enterprise tenant ראשון עם דרישות compliance (HIPAA, GDPR-strict).
  • L1 - L2: tenant אחד שובר את הבסיס - noisy neighbor.
  • L2 - L3: enterprise דורש data residency במדינה ספציפית או dedicated SLA.

חוזה ITenantScoped

כל ישות חייבת implement ITenantScoped { TenantId }. EF Core query filter גלובלי. Autotest - בודק שישות חדשה בלי TenantId לא קומפלת. בלי החוזה הזה L1 - חבית אבק.

איפה .NET 10 עוזר

  • AsyncLocal משופר - TenantContext דרך DI scope + AsyncLocal כבר לא hot path.
  • EF Core 10 query filters עם plan cache - בעבר filter per-tenant ביטל את התוכנית; עכשיו לא.
  • Native AOT (היכן שאפשר) למיקרו-שירותים - מוריד עלות tenant ב-L1 בערך 30%.

מה לקחת מכאן

  1. רוב SaaS-B2B צריך להתחיל ברמה 1. אל תסבכו.
  2. משמעת ITenantScoped + autotests - חוזה בלתי ניתן להפרה.
  3. הניחו מסלול מעבר L1 - L2 מהחודש הראשון.
  4. רמה 3 - רק במחיר enterprise. לעולם אל תיתנו "cluster נפרד" כבונוס שיווקי.