Design Patterns Tutorial 0/70 lessons ~6 min read Lesson 69

    Multi-Tenant SaaS Patterns

    Multi-tenant SaaS requires tenant-aware routing, auth, data, config, billing, audit.

    Course progress0%
    Focus
    21 guided sections
    Practice signal
    Examples included
    Career prep
    Interview Q&A included

    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:

    ts
    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:

    Three isolation models
    Shared schema
    tenant_id col
    Separate schema
    Per tenant
    Separate DB
    Enterprise
    Factory picks
    At runtime
    Start shared; Factory enables upgrade path without rewrite.
    Request tenant context
    JWT / subdomain
    Tenant ID
    Middleware
    Set TenantContext
    Repository
    Auto-scope queries
    Factory
    Tenant services
    Context propagates — never pass tenantId manually everywhere.
    Acme dedicated DB
    Enterprise deal
    Compliance
    TenantConfig
    dedicatedDb: true
    Factory → DedicatedRepo
    1 day
    Next 3 tenants
    1 hr each
    500-tenant story — Factory made enterprise tier plug-in.

    Informative example

    Before / after enterprise tenant:

    ts
    // ❌ tenant_id sprinkled; dedicated DB requires forked codebase
    const orders = await db.query("SELECT * FROM orders WHERE tenant_id = $1", [id])
    // ✅ Factory hides isolation model
    class 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

    1Multi-tenant evolution workflow
    1 / 4

    Shared schema + tenant_id

    Default for early SaaS.

    5 tenants.

    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.

    3 questions
    1AdvancedQuestionDesign multi-tenant SaaS data layer?+

    Answer

    TenantContext on request. Repository always scoped. Three isolation models. Factory selects repo impl from TenantConfig. Test cross-tenant isolation.

    Follow-up

    Enterprise dedicated DB?
    2AdvancedQuestionShared schema vs separate DB?+

    Answer

    Shared: cost-efficient, tenant_id column, noisy neighbor risk. Separate: compliance, isolation, ops cost. Factory enables both in one codebase.

    Follow-up

    Acme story?
    3AdvancedQuestionPrevent cross-tenant leak?+

    Answer

    Repository enforces tenant scope. Middleware sets context. Cache keys include tenant. Integration tests assert isolation.

    Follow-up

    Background jobs?

    Summary

    You can design flexible multi-tenant SaaS with enterprise upgrade path. Tell the 500-tenant Acme story — you own Multi-Tenant SaaS Patterns.

    Ready to mark this lesson complete?Track your journey across the entire course.