Skip to content
Mahesh Kadambala
Back to All Case Studies
Developer ToolsMaintained

Multi-Tenant SaaS Starter

Production-ready schema-per-tenant Spring Boot architecture starter

JavaSpring BootMulti-TenancyPostgreSQLArchitectureSecurityOpen Source

My Responsibility

Author & Architect

Engagement Timeline

2024

Project Artifacts

GitHub Repository

The Challenge

Building a B2B SaaS platform requires strict customer data isolation. However, implementing multi-tenancy from scratch is fraught with recurring failure modes:

  • Forgotten Tenant Predicates: In row-level tenancy models, a single query where a developer forgets to add where tenant_id = :id can expose private customer data to unauthorized users.
  • Async Context Loss: Standard ThreadLocal variables do not automatically propagate to pooled worker threads (e.g., @Async methods or scheduled jobs), causing background processes to execute against undefined or incorrect schemas.
  • Operational Complexity of DB-per-Tenant: Running a separate database instance for every customer introduces unsustainable cloud costs and operational friction for early-to-growth stage SaaS products.

The Solution & Architecture

Multi-Tenant SaaS Starter establishes schema-per-tenant isolation as an automatic configuration concern:

[ Incoming HTTP Request ]
          │
          ▼
┌──────────────────────────────────────────────┐
│  Tenant Resolution Filter                    │
│  ├── Extracts tenant from Header / Subdomain │
│  ├── Validates against registered tenants    │
│  └── Throws immediate 401/403 if invalid     │
└──────────────────────────────────────────────┘
          │ (TenantContext Bound)
          ▼
┌──────────────────────────────────────────────┐
│  AbstractRoutingDataSource                   │
│  ├── Resolves current tenant key             │
│  └── Routes query to tenant's schema pool    │
└──────────────────────────────────────────────┘
          │
          ▼
┌──────────────────────────────────────────────┐
│  Async TaskDecorator Propagation             │
│  ├── Copies TenantContext to worker threads  │
│  └── Safely clears ThreadLocal on completion │
└──────────────────────────────────────────────┘
Schema-per-Tenant Routing PipelineBinds database schemas dynamically per request, enforcing physical isolation at the connection level.Makes cross-tenant data leakage structurally impossible without incurring the heavy infrastructure cost of dedicated databases.

Key Architectural Invariants:

  1. Fail-Closed by Design: If a request or background task reaches the data layer without an explicitly verified tenant context, the system throws an immediate runtime exception rather than falling back to a default tenant.
  2. Thread-Safe Async Execution: Custom TaskDecorator wrappers ensure that any asynchronous task (CompletableFuture, @Async, scheduled jobs) inherits the parent tenant context and deterministically cleans it up when returning to the thread pool.
  3. Pluggable Tenant Resolvers: Built-in support for HTTP header resolution (X-Tenant-ID), subdomain routing (tenant.yourdomain.com), and custom JWT claim extractors.

Verified Results & Developer Impact

  • Zero Cross-Tenant Leaks: Provides rock-solid logical and physical data isolation for enterprise compliance audits (SOC2, HIPAA readiness).
  • Instant SaaS Setup: Reduces multi-tenant backend setup time from weeks of error-prone configuration down to a few lines of configuration.
  • Tested with Real Infrastructure: Comprehensive integration tests run against real PostgreSQL schemas using Testcontainers.

Building something similar for your product?

I help SaaS founders and engineering leaders design, scale, and modernize complex backend architectures. Let's discuss how to apply these patterns to your system.

Case Study published Apr 1, 2024