Arconia Multitenancy
Arconia Multitenancy adds multitenancy to Spring Boot applications. It resolves the tenant of each request, makes it available throughout your code and to the background tasks the request starts, and adds it to logs and observations. It is a set of composable modules built on Spring Boot auto-configuration, with sensible defaults, so you can adopt only the parts you need.
Arconia Multitenancy requires Java 25+ because it uses the ScopedValue API (JEP 506) for tenant context propagation. All the other Arconia modules still support Java 21, but if you want to use Arconia Multitenancy, your application must run on Java 25 or later.
|
Quick Start
The most common way to get started is with the Web starter, which provides HTTP-based tenant resolution for servlet applications.
Dependencies
Add the Arconia Multitenancy Web Spring Boot Starter dependency to your project.
-
Gradle
-
Maven
dependencies {
implementation 'io.arconia:arconia-multitenancy-web-spring-boot-starter'
}
<dependency>
<groupId>io.arconia</groupId>
<artifactId>arconia-multitenancy-web-spring-boot-starter</artifactId>
</dependency>
Arconia publishes a BOM (Bill of Materials) that you can use to manage the version of the Arconia libraries. It is highly recommended to use the BOM to ensure that all dependencies are compatible.
-
Gradle
-
Maven
dependencyManagement {
imports {
mavenBom "io.arconia:arconia-bom:0.32.0"
}
}
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.arconia</groupId>
<artifactId>arconia-bom</artifactId>
<version>0.32.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Running the Application
You can run your Spring Boot application using your preferred method.
-
Gradle
-
Maven
-
CLI
./gradlew bootRun
./mvnw spring-boot:run
arconia dev
Sending a Request
By default, Arconia Multitenancy resolves the current tenant from the X-TenantId HTTP header. Include this header in your requests to identify the tenant.
-
HTTPie
-
curl
http :8080/api/resource X-TenantId:acme
curl -H "X-TenantId: acme" http://localhost:8080/api/resource
If the header is missing, the server responds with HTTP 400 Bad Request.
Out of the box, any identifier that passes the identifier validation is accepted, so the client can name any tenant, and the application logs at startup that tenants are not verified. To accept only the tenants you know, configure tenant details, for example, as a list in the application configuration or in a database.
Accessing the Current Tenant
You can access the current tenant identifier in your controllers using the @TenantIdentifier annotation on a method parameter.
import io.arconia.multitenancy.web.context.TenantIdentifier;
@RestController
class GreetingController {
@GetMapping("/greeting")
Greeting greeting(@TenantIdentifier String tenantId) {
return new Greeting("Hello", tenantId);
}
record Greeting(String message, String tenant) {}
}
Alternatively, you can access the tenant identifier anywhere in your code using the TenantContext.
import io.arconia.multitenancy.core.context.TenantContext;
String tenantId = TenantContext.getRequiredTenantIdentifier();
The Architecture page explains how the tenant goes from the request to the TenantContext, and what happens when it cannot.
Modules
Arconia Multitenancy is organized into modules, each addressing a specific concern. You can use them independently or combine them as needed.
Core
The core module (arconia-multitenancy-core-spring-boot-starter) provides the foundational building blocks for multitenancy, independent of any specific application layer:
-
Tenant Context: Scoped context propagation via
TenantContextusing Java’sScopedValueAPI, including propagation to asynchronous tasks. -
Tenant Details: Tenant identifier validation via
TenantIdentifierValidator, tenant metadata management viaTenantDetailsService, and verification viaTenantVerifier. -
Caching: Tenant-aware cache key generation for cache isolation between tenants.
-
Observability: SLF4J MDC logging enrichment and Micrometer observation integration.
If you are building a non-web application (e.g., a message consumer or a batch job), you can use the core starter directly:
-
Gradle
-
Maven
dependencies {
implementation 'io.arconia:arconia-multitenancy-core-spring-boot-starter'
}
<dependency>
<groupId>io.arconia</groupId>
<artifactId>arconia-multitenancy-core-spring-boot-starter</artifactId>
</dependency>
Web
The web module (arconia-multitenancy-web-spring-boot-starter) adds HTTP-specific multitenancy support for Spring MVC applications:
-
Tenant resolution from headers, cookies, paths, subdomains, or OAuth2 tokens, combined in a configurable chain, with an optional default tenant.
-
A servlet filter (
TenantContextFilter) that establishes the tenant context for each request, with a pluggable handler for requests whose tenant cannot be established. -
The
@TenantIdentifierannotation for injecting the tenant into controller methods.
The web starter includes the core module, so all core features are available automatically.
Tenant Details JDBC
The Tenant Details JDBC module (arconia-multitenancy-details-jdbc-spring-boot-starter) loads the tenant details from a relational database, so that tenants can be added, changed, and disabled while the application runs.
Data JDBC
The Data JDBC module (arconia-multitenancy-data-jdbc) adds tenant-aware data access for the database-per-tenant isolation strategy:
-
A
TenantDataSourcethat routes each connection request to the database belonging to the current tenant. -
Per-tenant data sources registered upfront or created on demand and cached.
It builds on plain JDBC and works with JdbcClient, JdbcTemplate, and Spring Data JDBC. Unlike the other modules, it ships no auto-configuration, so it has no starter: add the artifact and declare the TenantDataSource bean yourself.