TENANCY FEATURE
Multi-Layer Tenancy
Four independent layers compose into one TenantContext, so tenant identity, locale, theme, and environment stay explicit instead of collapsing into one switch.
Feature Guide
A quick orientation block that answers the essential questions: what this feature does, how it works, why it matters, and the key concepts behind it.
What this does
Tenant context is not one switch. It is a composed stack of organization, locale, theme, and environment decisions.
How it works
Each layer resolves independently, then merges into the final context consumed by the rest of the app.
Why it matters
When a page renders in the wrong language or theme, you can see which layer decided it instead of guessing from one opaque tenant value.
Organization → Locale → Theme → Environment. Each layer answers a different product question, and together they form one explicit TenantContext.
This page is not about stacking abstractions for their own sake. It shows how tenancy stops being mysterious when each decision is isolated, named, and resolved once.
Resolve organization first
The runtime must decide whose request this is before any other tenant-aware behavior makes sense.
Derive locale and theme independently
Language and branding are separate concerns. One can change without forcing the other to be hard-wired.
Keep environment as a layer, not a hidden global
Production, staging, or preview behavior should be explicit in the tenant context instead of leaking through conditionals.
OrganizationLayer
Identifies which tenant (organization) owns this request.
LocaleLayer
Determines the preferred language for this tenant request.
ThemeLayer
Loads per-tenant branding — colors, fonts, feature flags.
EnvironmentLayer
Identifies the deployment stage for this tenant.
Organization × Theme Matrix
Once the layers are composed, each tenant gets a consistent final surface without rebuilding the rules in handlers or templates.
| Tenant | Organization | Theme | Locale | Color |
|---|---|---|---|---|
| Acme Corp | acme |
acme-theme |
en |
#1e40af |
| Globex Inc | globex |
globex-theme |
de |
#166534 |
| Initech Ltd | initech |
initech-theme |
uk |
#c2410c |
MultilayerTenantResolver internals
Each LayerDefinition pairs a layer type with a resolver strategy. The resolver runs all strategies, builds each layer independently, then merges them into a single immutable TenantContext.
The important architectural habit is this: downstream code reads the composed context, not the mechanics of how each layer was produced. Layers are resolved in order, but remain conceptually independent.