Backend Development 14 min read

Master Multi‑Tenant Architecture with Spring Boot & Spring Cloud

This article explains the concept, advantages, design choices, and step‑by‑step implementation of a multi‑tenant architecture using Spring Boot and Spring Cloud, covering database design, tenant isolation, dynamic routing, and deployment scenarios for SaaS and enterprise applications.

macrozheng
macrozheng
macrozheng
Master Multi‑Tenant Architecture with Spring Boot & Spring Cloud

Overview

Multi‑tenant architecture allows multiple tenants to access a single application while keeping each tenant's resources and data completely isolated. In simple terms, it partitions an application into independent instances per customer, preventing interference between them.

What is Multi‑Tenant Architecture?

It means supporting several tenants simultaneously, each with its own isolated data and resources.

Advantages of Multi‑Tenant Architecture

Better satisfies personalized needs of different tenants.

Reduces operational costs and infrastructure investment.

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

Improves scalability and extensibility, supporting horizontal scaling while managing each tenant's data and resources.

Technical Choices for Implementing Multi‑Tenant Architecture

Choosing the right technology speeds up implementation, though the architectural idea is the most critical factor.

Design Approach

Architecture Selection

For Java‑based multi‑tenant applications, Spring Boot and Spring Cloud are recommended. Spring Boot accelerates project setup with many mature plugins, while Spring Cloud provides tools for building micro‑service architectures.

Spring Boot

Spring Boot simplifies project bootstrapping and auto‑configures common third‑party libraries, reducing developer workload.

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

Spring Cloud

Spring Cloud offers mature solutions such as Eureka, Zookeeper, and Consul for service discovery and load balancing, which are useful in multi‑tenant systems.

Database Design

In a multi‑tenant environment, the database must store each tenant's data separately to ensure isolation. Two common approaches are:

Shared database with a

tenant_id

column in each table.

Separate database per tenant, with identical schemas.

Tenant Management

Managing tenant data and resources requires operations such as adding, updating, deleting, and querying tenant information, as well as assigning appropriate permissions.

<code>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
);
</code>
<code>@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");
    }
}
</code>

Technical Implementation

Multi‑Tenant in Spring Boot

Implement multi‑tenant support using multiple data sources and dynamic routing.

Multiple Data Source Configuration

<code>@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();
    }
}
</code>

Use the appropriate data source via

@Qualifier

:

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

Dynamic Routing

<code>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();
    }
}
</code>

Multi‑Tenant in Spring Cloud

Use Eureka for service registration and discovery, Spring Cloud Config for tenant‑specific configuration, and Ribbon for load balancing.

Application Scenarios

Private Cloud

Deploying multi‑tenant applications in a private cloud offers better data security and control for enterprises.

Public Cloud

Public cloud provides cost‑effective, elastic resources suitable for SaaS offerings.

Enterprise‑Level Applications

Enterprise systems such as ERP, CRM, and OA benefit from multi‑tenant designs to achieve high efficiency, reliability, and maintainability.

Implementation Steps

1. Set Up Spring Boot and Spring Cloud

Add the following Maven dependencies:

<code><!-- Spring Boot -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- Spring Cloud -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-dependencies</artifactId>
    <version>2020.0.3</version>
    <type>pom</type>
    <scope>import</scope>
</dependency>
</code>

Configure essential properties in

application.yml

:

<code>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: "*"
</code>

2. Modify Database Design

Add tenant‑related fields to tables to distinguish data per tenant.

3. Implement Multi‑Tenant Deployment

Instantiate tenant‑specific Spring beans and route requests based on tenant ID.

<code>@Configuration
public class MultiTenantConfig {
    // Provide data source per tenant
    @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();
    }
    // Dynamic tenant interceptor
    @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();
    }
}
</code>

4. Implement Tenant Management

Register each tenant's service instance with Eureka and provide an isolated database for each tenant to ensure data isolation.

Conclusion

This guide detailed how to build a multi‑tenant application using Spring Boot and Spring Cloud, covering environment setup, database redesign, deployment, and tenant management. The approach suits SaaS platforms and multi‑tenant cloud services, offering improved scalability and maintainability while introducing additional operational complexity.

JavaMicroservicesSpring Bootmulti-tenantSpring Cloud
macrozheng
Written by

macrozheng

Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.

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.