← Tenancy

TENANCY FEATURE

Data Isolation

The same repository call returns a different dataset when tenant context changes, without hand-written WHERE clauses in business code.

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

Switch tenant, and the same repository calls return a different dataset without hand-written WHERE clauses.

How it works

Tenant-scoped resources inject tenant filters automatically, so repository code stays focused on business queries.

Why it matters

One forgotten WHERE clause is how a customer sees another customer's data. When the platform applies the scope, no query has to remember it.

Switch tenant and the result set changes instantly. The point is not the table itself, but the fact that isolation happens below your business query.

This page demonstrates tenant-scoped persistence as a platform guarantee. Repositories ask for products; the framework decides which tenant slice is allowed to come back.

Switch tenant, keep the same query

The repository method does not change when the tenant changes. Only the active context changes.

Filtering belongs to the platform layer

The tenant WHERE clause is injected by the framework, so business queries are not polluted with isolation boilerplate.

The result should feel obviously different

A convincing demo must show that each tenant sees a distinct slice of data, not just a hidden architecture rule.

17 products for tenant acme

No products yet for this tenant. Run semitexa:demo:seed.

ORM automatic WHERE injection

Add #[TenantScoped(strategy: 'same_storage')] to any model and the framework enforces tenant scoping automatically across reads and writes.

SELECT * FROM demo_products
WHERE tenant_id = ?
LIMIT 5
#[FromTable(name: 'demo_products')]
#[TenantScoped(strategy: 'same_storage')]
final class DemoProductResource
{
    // No manual filtering. No forgotten WHERE clauses.
}

Three isolation strategies

same_storage

Shared table, automatic WHERE tenant_id = ? (#[TenantScoped])

database per tenant

The connection pool switches to the tenant's database when the tenant is resolved

The strategy is per-model, so the same app can mix lightweight shared-table isolation with stricter tenant storage boundaries where needed.