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 TenantContext using Java’s ScopedValue API, including propagation to asynchronous tasks.

  • Tenant Details: Tenant identifier validation via TenantIdentifierValidator, tenant metadata management via TenantDetailsService, and verification via TenantVerifier.

  • 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 @TenantIdentifier annotation 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 TenantDataSource that 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.