Multi-Tenancy in SaaS: Unlocking Its Potential with Spring Boot Java Framework and AWS
Given the complexity of balancing business objectives with technological demands, we have created this guide to help you choose and implement the optimal multi tenant architecture for SaaS applications using Java, Spring Boot, and AWS.
By the end of this article, you will understand how to select the optimal approach for your business requirements and how Romexsoft’s expertise can help you achieve your SaaS goals.
In particular, we will cover:
- What multi-tenancy in cloud computing is
- What the benefits and disadvantages of each multi tenant architecture are
- How to implement different types of architectures using Java, Spring Boot, and AWS
- How businesses can understand which type is the most suitable for their needs
Table of Contents
Table of Contents
What is Multi-Tenancy?
Known as a critical architectural strategy, it allows several users (or tenants) to use a single application instance simultaneously preserving data confidentiality and isolation. For this reason, multi-tenancy in cloud computing is frequently employed in SaaS (Software as a Service) settings.
The advantages of this architectural approach include optimal resource use, financial savings, trouble-free maintenance, and updates, which overall make the multi-customer framework a crucial part of modern software development.
From a business perspective, multi-tenancy in cloud computing offers cost savings, scalability, and adaptability, making it a popular choice for developing reliable, secure, and versatile applications.
How AWS Interprets Multi-Tenancy
Tenant isolation is crucial in designing and developing multi-tenancy applications, especially Software as a Service (SaaS). While multi-tenancy and SaaS are often intertwined, viewing them as synonymous can lead to a narrow, technical perspective on SaaS, which is more accurately a business model rather than merely an architectural strategy.
More specifically, the traditional view of multi-tenancy focuses on the infrastructural aspect, highlighting resource sharing among tenants to enhance agility and cost efficiency. For instance, a microservice or Amazon EC2 instance utilized by multiple tenants in a SaaS system exemplifies a multi-tenant model, where infrastructure is shared among users.
Equating multi-tenancy strictly with SaaS, however, can be misleading. This perspective assumes that SaaS inherently requires a shared multi-tenant infrastructure, which does not hold true across various SaaS implementations and tenant isolation models.
AWS offers a comprehensive suite of tools and services to support multi-tenant architectures in SaaS applications. AWS’s multi-tenancy approach includes Silo, Pool, and Bridge models, each providing different levels of isolation, cost efficiency, and operational complexity, thus enabling tailored solutions for diverse SaaS environments.
Silo Model
The basic characteristic of the Silo model is that each tenant has a completely segregated set of resources, such as compute, storage, and networking elements. Using this model grants the highest level of security and isolation, which are the result of each tenant functioning in their own assigned area.
Benefits
- Strong data separation: Each tenant’s data is entirely isolated, which minimizes the risk of data leaks.
- Flexibility: Individual tenant databases or schemas can be scaled according to their requirements, while maintaining independence.
- Simplified adherence and monitoring: Isolated schemas or databases help with easier performance examination and meeting the set requirements.
Disadvantages
- Increased resource consumption: Sustaining isolated schemas or databases for each tenant means more resource usage.
- Intricacies in management: Operating and sustaining numerous databases or schemas is more complex, much more so at scale.
- Higher operational overhead: Managing and providing resources for each tenant involves extra effort.
Whereas the model offers utmost levels of security and isolation, the resources required for each tenant and the higher management expenses associated with operating multiple separate environments make this model quite expensive.
The variation of Silo model could be Full-Stack Silo model where tenant isolation is ensured by running tenants in the isolated AWS Accounts under AWS organizations service.
Pool Model
Let’s delve into the Pool model, which provides shared resources for multiple tenants. This feature can not only drastically reduce costs but also make operating easier. Notably, when using this model, tenants share the same resources and infrastructure while data and access remain logically isolated.
Benefits
- Financial efficiency: Resources are being shared by all tenants lower overall expenses.
- Easier management: There is only one database that requires management and maintenance.
- Smart resource utilization: Available resources are shared by numerous tenants with maximum efficiency.
Disadvantages
- Possible data exposure: Strict data access controls are required to avoid tenants having access to each other’s data.
- Query operating intricacies: Tenant identifiers must be included in queries for thorough data isolation.
- Deterioration in performance: Increased number of tenants can cause decline in the efficiency of the database because of disagreements associated with shared resources.
Given the nature of the Pool model, it requires stringent data access controls and regulation mechanisms to guarantee that tenant data remains inaccessible to others. To address this need, AWS provides services that offer row-level security, which is essential for the effective deployment of this model. Services such as AWS Identity and Access Management (IAM) and Amazon RDS include tools designed to enforce these controls and maintain data isolation among tenants.
Bridge Model
Let’s explore the Bridge model, which blends elements of both the Silo and Pool models. In this approach, application servers are shared among tenants, while other components, such as databases, remain isolated to establish data separation. This configuration allows for efficient resource utilization while maintaining data isolation, creating a balanced model.
With shared infrastructure layers and separate databases, the Bridge model provides flexibility for alterations and customization across various components. The complexity of scaling in this model falls between that of the Pool and Silo models, offering a versatile solution for different use cases.
Benefits
- Increased Data Isolation: The model ensures thorough data isolation and lowers the risk of data leaks because of the databases being isolated.
- Resource Optimization: Due to infrastructure components (such as application servers) being shared, resources are used more effectively, possibly cutting operational expenses.
- Versatility: Combination of both shared and isolated elements according to tenants’ needs is possible because of the Bridge model being a hybrid approach.
- Scalability: This model provides better scalability options than the Silo model, even though it is more complex than the Pool model. This makes sure that each tenant can independently scale their database.
Disadvantages
- Higher Difficulty: There might be unnecessary complexity in operating and sustenance that comes from managing both isolated and shared components.
- More Expensive than Pool Model: While the Bridge model is more efficient than the Silo model, sustaining isolated databases is still more pricey than a fully pooled model.
- Possible Performance Constraints: So as no to create difficulties in shared components’ performance because of high tenant load, resources and management should be thoroughly planned.
- Operational Overhead: Unlike with a fully pooled model, here the demand for coordinating and supervising both shared and isolated elements enhances the overhead in operation.
AWS’s view on multi tenant meaning ensures that you find a model to fulfill your requirements concerning goals, operation and security. With AWS’s extensive set of services, you can design and deploy multi tenant architectures that achieve the right balance of financial optimization, scalability, and data isolation required for your SaaS application.
Architectural Approaches for Multi Tenant SaaS Platforms
Now that we have explored AWS’s approach to supporting shared customer applications, let’s dive into specific architectural approaches within the Java Spring Boot framework and examine practical examples.
Along with each of these techniques, it is necessary to build some support services and choose a general way of the back-end being integrated for each client. For this reason, consider the following application from our case study.
Key components
The Presentation layer includes Angular applications designed for various purposes: one for user sign-in/sign-up and the main SaaS application for end users across multiple tenants. Additionally, there is an Angular application for SaaS platform administrators. These applications provide the user interface for end users and administrators.
The API Gateway layer acts as an intermediary between the frontend and backend, comprising two key services:
- API Gateway Service: Built using the Spring Cloud Gateway project, this service simplifies routing to APIs while also handling their security, monitoring, metrics, and resiliency. It provides a straightforward and effective solution for managing API requests.
- Authentication Service: Created with the Spring Security framework, this service implements JWT (JSON Web Token) authentication, based on the open standard RFC 7519. JWT is a compact, URL-safe method for securely transmitting claims between parties. This mechanism allows the server to verify a user’s identity and convey roles and permissions, ensuring secure communication.
How JWT Works
A JWT (JSON Web Token) includes three parts: Header, Payload, and Signature.
- Header:
This part specifies the algorithm used for signing the token, such as HMAC SHA256 or RSA. - Payload:
Contains the claims, which provide information about the user and additional metadata. This can also include custom fields, such as TenantId. - Signature:
Ensures the token’s integrity by verifying that it hasn’t been altered. It is generated using a secret or private key to secure the information in the Header and Payload.
Upon user login, the server generates a JWT and sends it to the client. The client then includes this token in the Authorization header of subsequent HTTP requests. The server verifies the token’s signature to authenticate the user and validate their rights.
Adding TenantId to JWT Claims
This is the algorithm for implementing JWT authentication with tenant-specific data in multi-tenancy Spring Boot.
- Generate JWT with TenantId Claim:
Include the TenantId in the JWT claims during the authentication. - Extract TenantId from JWT:
When a request is received, extract the TenantId from the JWT claims to isolate the data. - Set Tenant Context in Spring Security:
Then, use a filter to choose the tenant context, which depends on the extracted TenantId for each of your requests. - TenantContext Management:
Monitor the tenant context to establish and maintain information about which tenant is currently interacting with the system.
public String generateToken(UserDetails userDetails, String tenantId) {
Mapclaims = new HashMap<>();
claims.put("tenantId", tenantId);
return Jwts.builder()
.setClaims(claims)
.setSubject(userDetails.getUsername())
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + JWT_TOKEN_VALIDITY))
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
public String getTenantIdFromToken(String token) {
final Claims claims = getAllClaimsFromToken(token);
return claims.get("tenantId", String.class);
}
private Claims getAllClaimsFromToken(String token) {
return Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
}
public class JwtTokenFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
final String requestTokenHeader = request.getHeader("Authorization");
String jwtToken = null;
String tenantId = null;if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
jwtToken = requestTokenHeader.substring(7);
tenantId = jwtTokenUtil.getTenantIdFromToken(jwtToken);
}if (tenantId != null) {
TenantContext.setCurrentTenantId(tenantId);
}chain.doFilter(request, response);
}
}
public class TenantContext {private static final ThreadLocal
currentTenant = new ThreadLocal<>(); public static void setCurrentTenantId(String tenantId) {
currentTenant.set(tenantId);
}public static String getCurrentTenantId() {
return currentTenant.get();
}
}
JWT authentication affirms that each request is securely verified and authenticated. Additionally, this setup supports tenant-specific claims and maintains proper data isolation for each tenant.
Back-End Layer: Applications are deployed in containers and designed as microservices, with a separate database for each service. This approach keeps data isolated, avoids inter-service constraints, and is responsible for keeping the entire infrastructure intact in case an issue in one part of the system arises. We will further explore how different multi-tenant models can be implemented within this setup.
Despite being a multi tenant model, all implementations include shared services such as Tenant Registration and Tenant Management. These services handle tasks like managing subscriptions and are accessible to SaaS platform administrators through the SaaS Provider Admin console.
Next, we will delve into the implementation of various multi-tenant models using the Java Spring framework.
Shared Database with Row-Level Isolation (Pooled Model)
In this approach, the database and tables are shared among all tenants, but each tenant’s data remains isolated through the use of a tenant identifier column (tenantUuid). This method is often referred to as the Pooled Model.
Data for each tenant is stored in the same tables but is segregated by adding a tenantUuid to each row, achieving “row-level isolation.” Effective management is crucial to prevent tenants from accessing each other’s data.
When implementing this model with Java, Spring Boot, and Hibernate, the @TenantId
annotation is used to ensure row-level data isolation. This involves marking users with the @TenantId
annotation and configuring Hibernate to handle data operations according to the tenant currently interacting with the system.
Implementation Details
The @TenantId
annotation automatically labels entities with the tenant identifier, thus guaranteeing each entity instance being associated with a specific tenant, while Hibernate adjusts the data according to the tenant.
Entity Configuration
Use the @TenantId
annotation to label a specific field in your entity depending on the tenant.
import org.hibernate.annotations.TenantId;@Entity
public class Customer {@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;@TenantId
private String tenantId;private String name;
// Getters and setters
}
CurrentTenantIdentifierResolver Implementation
Implement CurrentTenantIdentifierResolver
to resolve the current tenant identifier from the tenant context.
import org.hibernate.context.spi.CurrentTenantIdentifierResolver;public class TenantIdentifierResolver implements CurrentTenantIdentifierResolver {
@Override
public String resolveCurrentTenantIdentifier() {
return TenantContext.getCurrentTenantId();
}@Override
public boolean validateExistingCurrentSessions() {
return true;
}
}
Hibernate Configuration
Set up Hibernate to use the tenant identifier resolver.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;@Configuration
public class HibernateConfig {@Bean
public HibernateJpaVendorAdapter hibernateJpaVendorAdapter() {
HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
adapter.setJpaPropertyMap(Map.of(
"hibernate.tenant_identifier_resolver", new TenantIdentifierResolver()
));
return adapter;
}
}
Set Tenant Context
Management of TenantContext and how it is extracted from requests is detailed in the JWT configuration section above.
Summary
By integrating the @TenantId annotation and configuring Hibernate for multi tenant cloud environments, you can achieve tenant-specific data isolation while using a shared database. This approach combines the benefits of row-level data separation with shared resources, allowing for effective data management and operational efficiency.
Separate Database Schema/Database Per Tenant (Silo Model)
Often known as the Silo Model, this approach allocates a dedicated schema or a separate database to each tenant within a single database system. This ensures robust data isolation, simplifies compliance, and streamlines auditing processes, as each tenant’s data remains securely separated. Additionally, this method allows for independent scaling of tenant databases or schemas based on specific needs, offering flexible resource management.
When developing with Java and Spring Boot, data isolation can be effectively implemented either at the database schema level or by using separate RDS instances, depending on your requirements.
Implementation Details
Dynamic DataSource Configuration
When implementing a separate schema or database for each tenant, your application needs a method to switch between these data sources, known as the routing data source. In a Spring Boot application, this can be achieved by configuring a routing data source that dynamically selects the appropriate data source based on the tenant currently using the system.
Here is a suggested approach to implement this:
Define the Routing DataSource
The routing data source extends AbstractRoutingDataSource
and uses the determineCurrentLookupKey
method to return the current tenant identifier, which is then used This tenant identifier helps to decide which data source, be it schema or database, to use for the current request.
public class TenantRoutingDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return TenantContext.getCurrentTenant();
}
}
Configure DataSources
Define and set up the data sources for each tenant, which will later be linked to the tenant identifiers.
@Configuration
public class DataSourceConfig {@Bean
@Primary
public DataSource dataSource() {
Map// Configure the data sources for each tenant
DataSource tenant1DataSource = createDataSource("jdbc:mysql://localhost:3306/tenant1db", "username", "password");
DataSource tenant2DataSource = createDataSource("jdbc:mysql://localhost:3306/tenant2db", "username", "password");targetDataSources.put("tenant1", tenant1DataSource);
targetDataSources.put("tenant2", tenant2DataSource);TenantRoutingDataSource tenantRoutingDataSource = new TenantRoutingDataSource();
tenantRoutingDataSource.setDefaultTargetDataSource(tenant1DataSource);
tenantRoutingDataSource.setTargetDataSources(targetDataSources);return tenantRoutingDataSource;
}private DataSource createDataSource(String url, String username, String password) {
HikariConfig config = new HikariConfig();
config.setJdbcUrl(url);
config.setUsername(username);
config.setPassword(password);
return new HikariDataSource(config);
}
}
Set Tenant Context
TenantContext Management and its extraction from request are detailed in the section with JWT configuration above, just like with Pooled Model.
This implementation approach could be applied to the Bridge Multi tenant model as well.
Summary
The essence of this method involves configuring data sources and routing them according to the current tenant context. This setup keeps the data isolated for each tenant within their own schema or database. It also supports efficient management of multi tenant applications, optimizing resource use and ensuring compliance with data privacy regulations.
Separate AWS Account Per Tenant (Full Silo Model)
Another approach is known as the Full Silo Model, which focuses on providing each tenant with a separate AWS account, offering complete resource isolation. Utilizing separate AWS accounts for each tenant ensures the highest level of isolation and security, as resources are fully separated. Although this approach allows for resource allocation to be tailored to each tenant’s needs and requirements, it can lead to higher costs and increased complexity when managing multiple AWS accounts.
Implementing the Full Silo Model involves infrastructure development, which can be better understood through our dedicated case study.
Trade-off analysis of Multi-Tenancy in SaaS for Business
Selecting the best-suiting multi tenant model for your SaaS application is crucial for optimizing price, complexity, performance, and security. To assist you in choosing the best approach for your business needs, we have developed a comprehensive analysis tailored to help you make an informed decision.
Shared Database with Row-Level Isolation (Pooled Model) – Best Fit For Startups and Small Businesses
The Pooled Model is ideal for companies aiming to reduce costs and alleviate operational challenges. This approach enhances resource utilization efficiency and simplifies management, making it particularly well-suited for businesses that are either just starting out or facing operational constraints.
Separate Database Schema/Database Per Tenant (Silo Model) – Best Fit For Medium-Sized Enterprises
This model is appropriate for businesses that prioritize strong data isolation and scalability. Its key advantage lies in balancing these features, making it an excellent choice for growing companies that need to scale individual tenant resources independently.
Separate AWS Account Per Tenant (Full Silo Model) – Best Fit For Large Enterprises and Regulated Industries
It proves ideal for organizations with strict compliance requirements and a need for the highest level of data protection and isolation, this model guarantees maximum security and compliance advantages. It caters to businesses that prioritize stringent regulatory standards and tailored resource management.
Bridge Model – Best Fit For Businesses Seeking Balance
This model is most suitable for organizations that require absolute compliance and the highest levels of data isolation and security, which are key characteristics of Full Silo Model.
Selecting the best approach to shared tenancy for your SaaS application involves balancing cost, operational challenges, and security levels. This decision depends on understanding which model aligns with your business’s future growth.
The complexity of choosing the right architectural model necessitates a wide range of specialists, including developers and DevOps engineers. Additionally, thorough software development requires proper testing, reliable CI/CD flows, and sophisticated design.
At Romexsoft, we provide all the essential tools and expertise for developing a proficient multi tenant SaaS platform. Our team of experienced developers, skilled DevOps engineers, and design experts work together to create an efficient solution using Java, Spring Boot, and AWS, – depending on the specific project requirements.