Implementing Multi‑Tenant Architecture with Spring Boot and Spring Cloud
This article explains the concept, advantages, design considerations, and step‑by‑step implementation of a multi‑tenant architecture using Spring Boot and Spring Cloud, covering database strategies, tenant isolation, dynamic routing, and deployment scenarios for SaaS applications.
Overview
Multi‑tenant architecture allows multiple tenants to access a single application while keeping each tenant's resources and data isolated. It enables personalized solutions, reduces operational costs, saves development effort, and improves scalability.
Advantages
Better meet diverse tenant requirements.
Lower infrastructure and maintenance costs.
Reuse code to accelerate onboarding of new tenants.
Enhanced scalability through horizontal expansion.
Technical Choices
The most important factor is a correct architectural approach; however, choosing appropriate technologies speeds up implementation.
Design Approach
Framework Selection
For Java‑based multi‑tenant applications, Spring Boot and Spring Cloud are recommended. Spring Boot simplifies project setup, while Spring Cloud provides tools for micro‑service architecture.
Spring Boot
Spring Boot auto‑configures common libraries, reducing developer effort.
@RestController
public class TenantController {
@GetMapping("/hello")
public String hello(@RequestHeader("tenant-id") String tenantId) {
return "Hello, " + tenantId;
}
}Spring Cloud
Spring Cloud offers solutions such as Eureka, Zookeeper, and Consul for service discovery and load balancing, which are useful in multi‑tenant systems.
Database Design
Two common strategies ensure data isolation:
Shared database with a tenant_id column in each table.
Separate database per tenant with identical schemas.
Application Deployment
Application Isolation
Isolate tenants using containers, virtual machines, or namespaces; Docker is a popular choice.
Tenant‑Specific Configuration
Store configuration per tenant (e.g., ports, SSL certificates) in a database or cloud configuration center.
Tenant Management
Tenant Information Maintenance
Provide CRUD operations for tenant metadata.
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 Permission Control
Enforce access control so 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");
}
}Technical Implementation
1. Multi‑Tenant in Spring Boot
1.1 Multiple DataSources
Configure a separate DataSource for each tenant.
@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 DataSource using @Qualifier :
@Service
public class ProductService {
@Autowired
@Qualifier("dataSourceA")
private DataSource dataSource;
// ...
}1.2 Dynamic Routing
Switch DataSources at runtime based on the tenant identifier.
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();
}
}2. Multi‑Tenant in Spring Cloud
Leverage service registration, configuration center, and load balancing to isolate tenants.
Use Eureka for service discovery; register each tenant's services under distinct names.
Store tenant‑specific configuration in Spring Cloud Config.
Employ Ribbon for tenant‑aware load balancing.
Implement tenant routing at the API gateway level.
Application Scenarios
Private Cloud
Deploy within an enterprise‑controlled environment for higher data security and control.
Public Cloud
Benefit from lower costs, elastic scaling, and global deployment.
Enterprise‑Level SaaS
Support ERP, CRM, OA, and other large‑scale applications requiring high reliability and security.
Implementation Steps
1. Set Up Spring Boot & Spring Cloud
Add Maven dependencies for Spring Boot and Spring Cloud.
org.springframework.boot
spring-boot-starter-web
org.springframework.cloud
spring-cloud-dependencies
2020.0.3
pom
importConfigure properties in application.yml (datasource URL, credentials, Eureka server, etc.).
2. Modify Database Schema
Add a tenant identifier column to relevant tables or create separate databases per tenant.
3. Implement Multi‑Tenant Deployment
Instantiate tenant‑specific beans and route requests based on tenant ID.
@Configuration
public class MultiTenantConfig {
// Provide tenant‑aware DataSource
@Bean
public DataSource dataSource(TenantRegistry tenantRegistry) {
return new TenantAwareDataSource(tenantRegistry);
}
// Multi‑tenant SqlSessionFactory
@Bean(name = "sqlSessionFactory")
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
return sessionFactory.getObject();
}
// Interceptor for tenant switching
@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's service instances and provide isolated databases for data separation.
Conclusion
The article detailed how to build a multi‑tenant application with Spring Boot and Spring Cloud, covering environment setup, database design, deployment, and management. Such architecture improves scalability and maintainability for SaaS solutions, though it adds operational complexity that can be mitigated with further automation.
Code Ape Tech Column
Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn
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.