Multi-Tenant SaaS Patterns
Multi-tenant SaaS requires tenant-aware routing, auth, data, config, billing, audit.
Introduction
Multi-tenant SaaS requires tenant-aware routing, auth, data, config, billing, audit. Three isolation models — shared schema, separate schema, separate DB — each with Repository + Strategy + Factory patterns.
The story
SaaS grew 5 → 500 tenants in 18 months. tenant_id everywhere worked until Acme Corp demanded separate DB for compliance. Rebuild: TenantContext + per-tenant Repository via Factory at request time. Acme's separate DB: 1 day. Next 3 enterprise tenants: 1 hour each.
The business problem
Tenant model mistakes become expensive rewrites at enterprise sales:
- Compliance blockers: enterprise requires dedicated DB.
- Noisy neighbor: one tenant's query slows all.
- Config sprawl: if/else per tenant feature flags.
- Data leak risk: missing tenant_id filter — catastrophic.
The problem teams faced
Multi-tenant architecture forces:
- Tenant identity on every request — context propagation.
- Pluggable isolation: shared → dedicated without rewrite.
- Per-tenant feature/config differences.
- Audit and billing per tenant.
Understanding the topic
Intent: Flexible tenant isolation with pattern composition.
- TenantContext — request-scoped tenant identity.
- Repository — queries always scoped by tenant.
- Strategy — per-tenant feature behavior.
- Factory — compose tenant-specific service graph at request time.
Internal architecture
Tenant-aware repository factory:
interface TenantContext { tenantId: string; config: TenantConfig }class RepositoryFactory {createOrderRepo(ctx: TenantContext): OrderRepository {if (ctx.config.dedicatedDatabase)return new DedicatedDbOrderRepo(this.poolFor(ctx.tenantId))return new SharedSchemaOrderRepo(this.sharedPool, ctx.tenantId)}}// Middleware: req.tenantContext = resolveTenant(req)
Visual explanation
Three diagrams cover isolation models, TenantContext flow, enterprise DB:
Informative example
Before / after enterprise tenant:
// ❌ tenant_id sprinkled; dedicated DB requires forked codebaseconst orders = await db.query("SELECT * FROM orders WHERE tenant_id = $1", [id])// ✅ Factory hides isolation modelclass OrderService {constructor(private factory: RepositoryFactory, private ctx: TenantContext) {}async listOrders() {const repo = this.factory.createOrderRepo(this.ctx)return repo.findAll() // repo enforces tenant scope internally}}
Execution workflow
Shared schema + tenant_id
Default for early SaaS.
Real-world use
Salesforce multi-tenant architecture; Slack workspace isolation; Shopify shop-scoped data; enterprise single-tenant deployments via config.
Production case study
5 → 500 tenant growth:
- Early: shared schema + tenant_id.
- Enterprise: Acme required dedicated DB.
- Factory: 1 day Acme; 1 hour each for next 3 enterprise tenants.
Trade-offs
- Pro: upgrade path to enterprise isolation; single codebase.
- Con: Factory complexity; test matrix per isolation model.
Decision framework
- Start shared schema until enterprise deal requires dedicated.
- TenantContext mandatory middleware — not optional parameter.
- Strategy for per-tenant feature variation — not if/else on tenantId.
Best practices
- Integration test: tenant A cannot read tenant B — ever.
- Repository enforces scope — don't rely on caller passing tenantId.
- TenantConfig drives Factory — not hardcoded tenant checks.
Anti-patterns to avoid
- if (tenantId === 'acme') dedicatedDb() — use config.
- Missing tenant filter in one query — data leak.
- Separate codebase per enterprise tenant.
Common mistakes
- Background jobs lose TenantContext — propagate explicitly.
- Cache keys without tenant prefix — cross-tenant cache leak.
Debugging tips
- Log tenantId on every query in dev — find unscoped queries.
Optimization strategies
- Connection pool per dedicated DB tenant; shared pool for small tenants.
Common misconceptions
- Multi-tenant ≠ always shared DB — three models coexist.
- Factory here composes tenant graph — not object creation only.
Advanced interview questions
Interview Prep
Practice concise answers, then expand each card for the explanation.
1AdvancedQuestionDesign multi-tenant SaaS data layer?+
Answer
Follow-up
2AdvancedQuestionShared schema vs separate DB?+
Answer
Follow-up
3AdvancedQuestionPrevent cross-tenant leak?+
Answer
Follow-up
Summary
You can design flexible multi-tenant SaaS with enterprise upgrade path. Tell the 500-tenant Acme story — you own Multi-Tenant SaaS Patterns.