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 = :idcan expose private customer data to unauthorized users. - Async Context Loss: Standard
ThreadLocalvariables do not automatically propagate to pooled worker threads (e.g.,@Asyncmethods 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 │
└──────────────────────────────────────────────┘
Key Architectural Invariants:
- 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.
- Thread-Safe Async Execution: Custom
TaskDecoratorwrappers 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. - 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.