implementing-multi-tenancy-in-a-spring-boot-application-with-postgresql.html

Implementing Multi-Tenancy in a Spring Boot Application with PostgreSQL

In today's cloud-driven world, building applications that serve multiple clients efficiently is paramount. This is where multi-tenancy comes into play. Multi-tenancy allows a single instance of a software application to serve multiple tenants (clients) while keeping their data isolated. In this article, we will explore how to implement multi-tenancy in a Spring Boot application using PostgreSQL, providing you with actionable insights, code examples, and best practices.

What is Multi-Tenancy?

Multi-tenancy is an architectural pattern where a single software application serves multiple users or clients (tenants). Each tenant has its own data, configurations, and possibly its own user interface, all while sharing the underlying application infrastructure.

Use Cases for Multi-Tenancy

  • SaaS Applications: Multi-tenancy is popular in Software as a Service (SaaS) applications where multiple customers use the same application instance.
  • Cost Efficiency: Reduces operational costs by sharing resources such as servers and databases.
  • Scalability: Simplifies scaling by allowing one application instance to handle multiple clients without significant changes to the codebase.

Types of Multi-Tenancy

  1. Database-per-Tenant: Each tenant has its own database. This provides strong isolation but can lead to high resource consumption.
  2. Schema-per-Tenant: Each tenant has its own schema within a shared database. This balances isolation and resource efficiency.
  3. Table-per-Tenant: All tenants share the same tables, and tenant identification is handled at the application level. This is less common due to complexity in ensuring data isolation.

In this article, we will focus on the Schema-per-Tenant approach using Spring Boot and PostgreSQL.

Setting Up the Spring Boot Application

Prerequisites

Before we dive into the code, ensure that you have the following set up:

  • Java Development Kit (JDK) 11 or later
  • PostgreSQL installed and running
  • An IDE (like IntelliJ IDEA or Eclipse)

Step 1: Create a Spring Boot Project

You can create a Spring Boot project using Spring Initializr. Include the following dependencies:

  • Spring Web
  • Spring Data JPA
  • PostgreSQL Driver

Step 2: Configure Application Properties

Open the application.yml (or application.properties) file and add your PostgreSQL configuration:

spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/your_db
    username: your_username
    password: your_password
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true

Step 3: Define the Multi-Tenancy Configuration

Create a configuration class to handle multi-tenancy. This class will determine the tenant identifier dynamically based on the incoming request.

import org.hibernate.context.spi.CurrentTenantIdentifierResolver;
import org.springframework.stereotype.Component;

@Component
public class TenantIdentifierResolver implements CurrentTenantIdentifierResolver {

    @Override
    public String resolveCurrentTenantIdentifier() {
        // Retrieve tenant identifier from context or request
        return TenantContext.getCurrentTenant();
    }

    @Override
    public boolean validateExistingCurrentSessions() {
        return true; // Always validate existing sessions
    }
}

Step 4: Implement Tenant Context

Create a TenantContext class to manage tenant identifiers:

public class TenantContext {
    private static final ThreadLocal<String> currentTenant = new ThreadLocal<>();

    public static void setCurrentTenant(String tenant) {
        currentTenant.set(tenant);
    }

    public static String getCurrentTenant() {
        return currentTenant.get();
    }

    public static void clear() {
        currentTenant.remove();
    }
}

Step 5: Intercept HTTP Requests

You need to intercept incoming requests to set the tenant identifier. Create a filter for that purpose:

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

public class TenantFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        String tenantId = ((HttpServletRequest) request).getHeader("X-Tenant-ID");
        if (tenantId != null) {
            TenantContext.setCurrentTenant(tenantId);
        }
        try {
            chain.doFilter(request, response);
        } finally {
            TenantContext.clear();
        }
    }
}

Register the Filter

In your main application class, register the filter:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class MultiTenancyApplication {

    public static void main(String[] args) {
        SpringApplication.run(MultiTenancyApplication.class, args);
    }

    @Bean
    public FilterRegistrationBean<TenantFilter> tenantFilter() {
        FilterRegistrationBean<TenantFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new TenantFilter());
        registrationBean.addUrlPatterns("/*");
        return registrationBean;
    }
}

Step 6: Create Entities

Now, let’s create an entity that will be shared across tenants:

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String email;

    // Getters and Setters
}

Step 7: Create Repositories

Create a repository for the User entity:

import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, Long> {
}

Step 8: Testing Multi-Tenancy

You can test multi-tenancy by sending HTTP requests with different X-Tenant-ID headers. Use tools like Postman or curl to make requests to your endpoints.

curl -H "X-Tenant-ID: tenant1" -X POST http://localhost:8080/users -d '{"name":"John Doe","email":"john@example.com"}'

Troubleshooting Common Issues

  • Data Isolation Issues: Ensure you are correctly setting and clearing the tenant context.
  • Database Connection Problems: Verify your PostgreSQL connection settings.
  • Performance: Monitor the application for any bottlenecks, especially with multiple tenants accessing shared resources.

Conclusion

Implementing multi-tenancy in a Spring Boot application with PostgreSQL can significantly enhance your application's scalability and efficiency. By following the steps outlined in this article, you can create a robust multi-tenant architecture that serves multiple clients while keeping their data secure and isolated. With careful planning and implementation, your application can thrive in a multi-tenant environment, making it a viable option for SaaS and other enterprise-level applications. Happy coding!

SR
Syed
Rizwan

About the Author

Syed Rizwan is a Machine Learning Engineer with 5 years of experience in AI, IoT, and Industrial Automation.