Reference Architecture
IllustrativeMulti-Tenant SaaS Platform
A SaaS architecture that separates the control plane from the application plane and carries tenant identity through every layer, from the edge to the data store.
The problem
Consider a software product sold to many organisations, from small teams on a self-service plan to large enterprises with security questionnaires and procurement processes. All of them expect their data to be invisible to every other customer, and the larger ones expect single sign-on, audit logs and predictable performance.
Serving every customer from a separate deployment is simple to reason about and expensive to run and upgrade. Serving them all from one shared deployment is efficient and makes isolation, fair resource use and per-customer configuration into engineering problems. The architecture has to choose where on that spectrum each tier of customer sits.
Requirements the design must meet
- One tenant must never be able to read or modify another tenant's data, and this must be enforced below the application code.
- A new tenant must be provisioned automatically, without an engineer performing manual steps.
- Enterprise tenants must be able to use their own identity provider and manage users through automated provisioning.
- Heavy usage by one tenant must not degrade the experience of others.
- Features, limits and billing must follow from a tenant's plan and be changeable without a deployment.
- Usage, errors and performance must be attributable to an individual tenant.
The design
Components
Tenant-aware Edge
- Custom domains
- Tenant resolution
- WAF
- Per-tenant rate limits
Identity & SSO
- SAML and OIDC
- SCIM provisioning
- Roles and permissions
- MFA
Control Plane
Manages tenants, never serves their traffic
- Tenant lifecycle
- Plans and entitlements
- Billing and metering
- Feature flags
Application Plane
- Stateless services
- Background jobs
- Tenant context
- Webhooks
Data Isolation
Isolation model chosen per plan tier
- Row-level security
- Schema per tenant
- Dedicated databases
- Per-tenant keys
Per-tenant Observability
- Usage metrics
- Tenant-tagged traces
- Noisy-neighbour alerts
- Audit logs
Tenant-aware edge
The edge resolves which tenant a request belongs to from its subdomain, custom domain or token, and rejects requests it cannot attribute. It applies rate limits per tenant so that one customer's traffic spike is contained. Custom domains and their certificates are issued and renewed automatically.
Identity and single sign-on
The identity layer supports password and MFA sign-in for smaller customers, and SAML or OIDC federation with SCIM provisioning for enterprises. Roles and permissions are defined per tenant. Every issued token carries the tenant identifier as a signed claim, which downstream services trust in place of any client-supplied value.
Control plane
The control plane owns the tenant lifecycle: sign-up, provisioning, plan changes, suspension and deletion. It holds the catalogue of plans and entitlements, and is the source of truth for what each tenant is allowed to use. It is deployed and scaled separately from the application plane, so that an incident in one does not take down the other.
Application plane
Stateless services handle product traffic and establish tenant context once, at the start of each request, from the verified token. That context is propagated to database sessions, background jobs, caches and outgoing events. Work queues are partitioned so that one tenant's backlog cannot starve another's jobs.
Data isolation
Standard tenants share a PostgreSQL database in which row-level security policies restrict every query to the current tenant. Tenants with stricter requirements can be placed in a dedicated schema or a dedicated database, selected by a routing layer. Cache keys, object storage paths and search indexes are all prefixed or partitioned by tenant.
Metering and billing
Services emit usage events, which are aggregated per tenant into the quantities that plans are priced on. The billing integration turns those into invoices through a payment provider, and entitlement checks read the same data to enforce limits. Usage events are idempotent, so retries do not result in double charging.
Per-tenant observability
Every log line, metric and trace is tagged with the tenant identifier. This allows support to see one customer's experience, and allows engineers to detect a noisy neighbour before others notice. An audit log of security-relevant actions is available to each tenant's administrators.
Trade-offs
Design decisions
| Decision | Why |
|---|---|
| A shared database with row-level security as the default | A database per tenant gives the strongest isolation and multiplies cost, migration time and operational work with every customer. Pooling with row-level security keeps the fleet small and upgrades simple, and relies on policies enforced by the database so that a missing filter in application code does not become a data leak. |
| Dedicated schemas or databases offered as a tier, not as the baseline | Some customers need isolation that pooling cannot provide, for contractual or residency reasons. Supporting more than one isolation model adds a routing layer and complicates migrations, so it is reserved for the tenants whose requirements justify it. |
| A control plane separate from the application plane | Folding tenant management into the product services is quicker to build at first. Separating it means two systems to deploy, and gives a clear place for provisioning, entitlements and billing that can change on its own schedule and fail without affecting customer traffic. |
| Tenant context derived from the signed token only | Accepting a tenant identifier from a header or request body is convenient and is a common source of cross-tenant access bugs. Taking it only from a verified claim removes that class of error, at the cost of making some internal tooling and support workflows more deliberate. |
| Entitlements evaluated at runtime instead of per-plan deployments | Shipping different builds per plan fragments testing and slows releases. A single codebase with runtime entitlement checks adds a lookup on the request path, which is cached, and allows plans and limits to change without a release. |
Intended outcomes
- Tenant isolation that is enforced by the platform, not by developer discipline
- Onboarding that is automated from sign-up to a working tenant
- Enterprise identity requirements that are met without custom work per customer
- Usage and performance that can be understood one tenant at a time
What the design is built to achieve. Illustrative, not measured results from a client project.
Representative technologies
- TypeScript
- Next.js
- Node.js
- Spring Boot
- PostgreSQL
- Redis
- Kafka
- Kubernetes
- Terraform
- OIDC / SAML / SCIM
- OpenTelemetry
Have a system that needs to work in production?
Tell us what you are building. An engineer will read it and reply within two working days.
