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.
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.
Architect's Guide
Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.