Skip to content
Development
Skill

/multi-tenancy

Use when implementing tenant resolution, database or schema isolation, tenant-aware JPA, reactive tenant context, migrations, caching, jobs, or authorization in Spring Boot 3.

From plugin
spring-boot-skills
33733 skills

Claude Code

$ npx -y skills add rrezartprebreza/spring-boot-skills --skill multi-tenancy --agent claude-code

SKILL.md

multi-tenancy.SKILL.md
name: multi-tenancy
description: >
  Use when implementing tenant resolution, database or schema isolation, tenant-aware JPA,
  reactive tenant context, migrations, caching, jobs, or authorization in Spring Boot 3.

Multi-Tenancy

Treat tenant identity as an authorization boundary, not a query convenience.

Choose an isolation model

  • Database per tenant: strongest isolation and highest operational cost.
  • Schema per tenant: strong logical isolation with shared infrastructure.
  • Shared schema with `tenant_id`: simplest operations, but every access path must enforce scope.
  • Document the selected model and prohibit repositories from bypassing it.

Resolve tenant identity

  • Derive the tenant from a verified token claim, trusted host mapping, or authenticated API key.
  • Reject missing, unknown, disabled, or conflicting tenant identifiers.
  • Never trust a public `X-Tenant-Id` header by itself.
  • Clear servlet thread-local context in `finally`; use Reactor `Context` for reactive flows.

Enforce isolation

  • Apply tenant selection before opening the persistence session or transaction.
  • Include tenant identity in unique constraints, cache keys, idempotency keys, and object storage paths.
  • Prevent cross-tenant joins and unrestricted administrative repositories.
  • Authorize support impersonation explicitly and audit every use.

Operations

  • Run migrations per database/schema with resumable progress and version reporting.
  • Propagate tenant identity into scheduled jobs, messages, and async tasks explicitly.
  • Limit noisy tenants with quotas and per-tenant observability using bounded identifiers.
  • Test negative cross-tenant access, not only successful tenant queries.

Examples

  • See `examples/good-tenant-filter.java` and `examples/bad-tenant-filter.java`.

Executable shared-schema example

The [TenantOrderStore](templates/TenantOrderStore.java) and [schema](examples/tenant-orders.sql) implement tenant-scoped reads, creates, updates and deletes. This example uses blocking JDBC, a verified resource-server JWT and an active-tenant registry. It is not a database/schema-routing or reactive implementation. Preserve the application's existing ID types and isolation model rather than copying this UUID model indiscriminately.

Wire [TenantScopeFilter](templates/TenantScopeFilter.java) into the security chain before `AuthorizationFilter` (after bearer authentication and exception translation). Supply a `RequestMatcher` for only the tenant-protected routes and a server-side predicate checking the tenant registry. Instantiate the filter inside the chain configuration, or disable its servlet auto-registration if it is a bean, so it does not run twice. Missing authentication is 401; missing, disabled or unknown tenant membership is 403. Do not read a public tenant header.

The verified `tenant_id` claim must be issued under a trusted tenant-membership policy; where revocation must take effect before token expiry, consult current membership as well as the active-tenant registry. Background jobs must similarly re-authorize tenant activity/membership at execution time. A captured ID is context propagation, not perpetual authorization.

[TenantContext](templates/TenantContext.java) scopes work with try/finally cleanup, preserves nested scopes and provides tenant-qualified cache keys:

TenantId tenant = TenantAccess.resolve(authentication, tenantRegistry::isActive);
try (var scope = TenantContext.open(tenant)) {
    store.find(orderId);
    String key = TenantContext.orderCacheKey(orderId);
    executor.execute(TenantContext.capture(() -> store.find(orderId)));
}

Open the tenant scope before invoking a separate transactional service proxy. The example does not propagate a transaction or SecurityContext to the worker; it starts its own unit of work. Do not use `InheritableThreadLocal` with pooled workers. Servlet async dispatches need explicit context propagation too. Scope every list, join and native query, and include `tenant_id` in child foreign keys. No Java repository wrapper protects against arbitrary unscoped SQL: restrict database credentials and consider PostgreSQL row-level security as defense in depth.

Verify same-ID records in two tenants, forbidden cross-tenant reads/updates/deletes, distinct cache keys, missing-context rejection, exception cleanup and sequential jobs on one worker.

Verification scope

The repository fixture targets Boot 3.5.16 with Java 17/21 and checks PostgreSQL 16 shared-schema access and blocking thread context; not Hibernate schema routing or Reactor. Check the [coverage matrix](https://github.com/rrezartprebreza/spring-boot-skills/blob/main/verification/MATRIX.md) before treating another minor version or an illustrative snippet as verified.

Gotchas

  • Agent trusts a tenant header supplied by the caller - derive tenant from authenticated context.
  • Agent forgets to clear a servlet `ThreadLocal` - pooled threads can leak one tenant into another request.
  • Agent scopes database queries but not cache keys - cached data can cross tenants.
  • Agent starts a transaction before selecting the tenant - routing may choose the wrong database.
  • Agent runs background jobs without tenant context - make tenant an explicit job parameter.
Read more
@rrezartprebreza
Ships withspring-boot-skills

Production-grade Claude Code and Codex skills for Spring Boot developers

Stars on GitHub

Momentum

stars in total
337
Star history of rrezartprebreza/spring-boot-skills on GitHub, a chart by star-history.com

Other skills on spring-boot-skills.

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills10d309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc12d293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react1d252k
@facebook@facebookInstall Skill

Questions about multi-tenancy.

Which coding agents can use multi-tenancy?
multi-tenancy installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.