Backend Development 13 min read

Implementing Multi‑Tenant Architecture with Spring Boot and Spring Cloud

This article explains the concepts, advantages, design choices, database strategies, and step‑by‑step implementation of a multi‑tenant system using Java Spring Boot and Spring Cloud, including code samples for data sources, dynamic routing, and tenant management in cloud environments.

Architect's Guide
Architect's Guide
Architect's Guide
Implementing Multi‑Tenant Architecture with Spring Boot and Spring Cloud

Overview

Multi‑tenant architecture allows a single application to serve multiple tenants, each with isolated resources and data, effectively partitioning the application into independent instances per customer.

Advantages

Better satisfies personalized needs of different tenants.

Reduces operation and infrastructure costs.

Saves development effort by reusing code and quickly launching new tenant instances.

Improves scalability and extensibility, supporting horizontal scaling while keeping tenant data isolated.

Technical Choices

While the architectural mindset is most important, selecting appropriate technologies accelerates implementation. Spring Boot and Spring Cloud are recommended for Java‑based multi‑tenant applications.

Design Ideas

Architecture Selection

Spring Boot provides rapid project setup and many mature plugins; Spring Cloud offers tools for building micro‑service architectures such as service discovery, configuration management, and load balancing.

Spring Boot

Spring Boot simplifies project scaffolding and auto‑configures common third‑party libraries.

@RestController
public class TenantController {
    @GetMapping("/hello")
    public String hello(@RequestHeader("tenant-id") String tenantId) {
        return "Hello, " + tenantId;
    }
}

Spring Cloud

Spring Cloud provides mature solutions like Eureka, Zookeeper, and Consul for service registration, discovery, and load balancing.

Database Design

Two common approaches for tenant data isolation:

Shared database with a tenant_id column in each table.

Separate database per tenant, with identical schema but isolated data.

CREATE TABLE tenant (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50) NOT NULL UNIQUE,
    description VARCHAR(255),
    created_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

Tenant Management

Operations include tenant information maintenance (add, modify, delete, query) and permission control to ensure tenants cannot access each other's data.

@EnableGlobalMethodSecurity(prePostEnabled = true)
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/api/tenant/**").hasRole("ADMIN")
            .anyRequest().authenticated()
            .and()
            .formLogin();
    }
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService())
            .passwordEncoder(new BCryptPasswordEncoder())
            .and()
            .inMemoryAuthentication()
            .withUser("admin")
            .password(new BCryptPasswordEncoder().encode("123456"))
            .roles("ADMIN");
    }
}

Implementation

Spring Boot Multi‑Tenant Implementation

Use multiple data sources and dynamic routing.

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

Inject the required data source with @Qualifier :

@Service
public class ProductService {
    @Autowired
    @Qualifier("dataSourceA")
    private DataSource dataSource;
    // ...
}

Dynamic routing class extends AbstractRoutingDataSource and determines the current tenant ID.

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

Spring Cloud Multi‑Tenant Implementation

Leverage service registration (Eureka), a configuration center (Spring Cloud Config), and load balancing (Ribbon) to isolate tenants.

Register each tenant's services with distinct application names in Eureka.

Store tenant‑specific configuration files in Config Server, keyed by tenant ID.

Use Ribbon to route requests to the appropriate tenant instance based on URL or parameters.

Application Scenarios

Private‑cloud environments for internal enterprise data.

Public‑cloud SaaS platforms offering cost‑effective, elastic services.

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

Implementation Steps

Set up Spring Boot and Spring Cloud dependencies in the Maven project. org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-dependencies 2020.0.3 pom import

Configure database connection and Eureka client in application.yml . 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: "*"

Modify the database schema to add a tenant identifier column.

Implement multi‑tenant deployment code. @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(); } }

Use Eureka to register each tenant instance and provide isolated databases for data separation.

Conclusion

The article provides a comprehensive guide to building a multi‑tenant application with Spring Boot and Spring Cloud, covering environment setup, database design, deployment techniques, and tenant management, making it suitable for SaaS, private‑cloud, and public‑cloud scenarios.

Javabackend architectureMicroservicesSpring Bootmulti-tenantSpring Cloud
Architect's Guide
Written by

Architect's Guide

Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.

0 followers
Reader feedback

How this landed with the community

login 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.