Skip to content
Spring Boot Multi-Tenant: Database Isolation, Dynamic DataSource Routing & Spring Cloud

Spring Boot Multi-Tenant: Database Isolation, Dynamic DataSource Routing & Spring Cloud

This article explains how to implement multi-tenant architecture using Spring Boot and Spring Cloud, covering database isolation strategies, dynamic data source routing with AbstractRoutingDataSource, tenant management, security configuration, and deployment considerations for SaaS applications.

Architect's Guide
Architect's Guide
Architect's Guide

Overview

Multi-tenant architecture allows a single application to serve multiple tenants with isolated resources and data. Benefits include meeting personalized tenant needs, reducing operational and development costs through code reuse, and enhancing scalability.

Design Approach

Technology Stack

Spring Boot simplifies project setup with auto-configuration; Spring Cloud provides service discovery (Eureka, Consul), configuration center, and load balancing for microservices.

Database Design

Shared database, shared schema: All tenants share tables with a tenant_id column for logical isolation.

Separate databases: Each tenant gets its own database with identical schema, ensuring physical isolation.

Application Isolation & Configuration

Isolation via containers (Docker) or VMs. Tenant-specific configurations (ports, SSL) stored in database or cloud config center.

Tenant Management

Tenant registry: CRUD operations on tenant metadata (ID, name, description). Example SQL for tenant table provided.

Permission control: Spring Security configuration restricting cross-tenant data access, using role-based access (e.g., ADMIN role for /api/tenant/**).

Technical Implementation

Spring Boot Multi-Tenancy

Multiple Data Sources

Define separate DataSource beans per tenant using @ConfigurationProperties with prefixes like spring.datasource.a. Inject via @Qualifier.

@Configuration
public class DataSourceConfig {
    @Bean(name = "dataSourceA")
    @ConfigurationProperties(prefix = "spring.datasource.a")
    public DataSource dataSourceA() {
        return DataSourceBuilder.create().build();
    }
    // dataSourceB, dataSourceC similarly
}

Dynamic Data Source Routing

Extend AbstractRoutingDataSource and override determineCurrentLookupKey() to return current tenant ID from a thread-local holder ( TenantContextHolder). Register as primary dataSource bean.

public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return TenantContextHolder.getTenantId();
    }
}

@Configuration
public class DataSourceConfig {
    @Bean(name = "dataSource")
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource dataSource() {
        return DataSourceBuilder.create().type(DynamicDataSource.class).build();
    }
}

Spring Cloud Multi-Tenancy

Service registration & discovery: Each tenant's services register under distinct application names in Eureka.

Configuration center: Spring Cloud Config serves tenant-specific configuration files.

Load balancing: Ribbon routes requests to appropriate tenant service instances.

API Gateway: Routes incoming requests to tenant-specific services based on URL or parameters.

Application Scenarios

Private cloud: Enterprise-owned, high data control.

Public cloud: Provider-managed, elastic scaling, global deployment.

Enterprise applications: ERP, CRM, OA systems requiring high reliability and security.

Implementation Steps

1. Project Setup

Add Spring Boot Web and Spring Cloud dependencies (version 2020.0.3). Configure application.yml with datasource URL, MyBatis, server port, Eureka client, and actuator endpoints.

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/appdb?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
    username: root
    password: 123456
mybatis:
  type-aliases-package: com.example.demo.model
  mapper-locations: classpath:mapper/*.xml
server:
  port: 8080
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/
management:
  endpoints:
    web:
      exposure:
        include: "*"

2. Database Modification

Add tenant-related fields (e.g., tenant_id) to tables for logical isolation.

3. Multi-Tenant Deployment Configuration

Core configuration class MultiTenantConfig provides:

Tenant-aware DataSource via TenantAwareDataSource.

Multi-tenant SqlSessionFactory. MultiTenantInterceptor with TenantResolver (e.g., HeaderTenantResolver reading tenant-id header). TenantRegistry implementation for tenant metadata.

@Configuration
public class MultiTenantConfig {
    @Bean
    public DataSource dataSource(TenantRegistry tenantRegistry) {
        return new TenantAwareDataSource(tenantRegistry);
    }

    @Bean(name = "sqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);
        return sessionFactory.getObject();
    }

    @Bean
    public MultiTenantInterceptor multiTenantInterceptor(TenantResolver tenantResolver) {
        MultiTenantInterceptor interceptor = new MultiTenantInterceptor();
        interceptor.setTenantResolver(tenantResolver);
        return interceptor;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(multiTenantInterceptor());
    }

    @Bean
    public TenantRegistry tenantRegistry() {
        return new TenantRegistryImpl();
    }

    @Bean
    public TenantResolver tenantResolver() {
        return new HeaderTenantResolver();
    }
}

4. Tenant Management

Use Eureka to register each tenant instance; provide separate databases per tenant for data isolation.

Summary

The article details building a multi-tenant SaaS application with Spring Boot and Spring Cloud. Key trade-offs: shared schema reduces resource overhead but requires strict query filtering; separate databases offer stronger isolation at higher operational cost. Dynamic routing via AbstractRoutingDataSource enables per-request tenant context switching. Spring Cloud components handle service discovery, configuration, and load balancing across tenants. The approach improves extensibility and maintainability but adds deployment complexity. Future improvements could automate tenant provisioning to reduce manual errors.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Spring Bootdynamic data sourceEurekaSpring Cloudmulti-tenancydatabase isolationSpring SecurityAbstractRoutingDataSource
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.