29. SaaS Architecture
SaaS Architecture
TL;DR
- Every SaaS architecture decision (multi-tenant design, billing, identity, incident response) reduces to the same question: are this tenant’s data, cost, and blast radius separated from every other tenant’s, at every layer of the stack.
- Pooled tenancy (shared infra) is cost-efficient but weaker on isolation; silo tenancy (dedicated resources) is strongly isolated but expensive per tenant. Most real products need a hybrid: pooled by default, silo as a tier for customers with a genuine isolation requirement.
- Application-layer
WHERE tenant_id = ?filtering alone is one missed query away from a cross-tenant data leak — database-layer row-level security is the required defense-in-depth backstop, not a redundant layer. - Billing needs tenant-level usage attribution at the point of consumption; a monthly aggregate AWS bill cannot answer “who do we invoice for what.”
- Shared identity infrastructure (single user pool + tenant_id attribute) is usually the right cost/complexity trade-off, but a bug in tenant-scoping logic risks every tenant simultaneously.
- Tenant context must be propagated through telemetry up front — it cannot be retrofitted quickly during an actual incident.
Core Concepts
Multi-tenant design: pooled vs. silo tenancy
Pooled tenancy shares infrastructure across tenants: cost-efficient and operationally simpler to run at scale, but weaker on isolation guarantees since separation depends on software correctness rather than physical/account boundaries. Silo tenancy dedicates resources per tenant: strong isolation, at meaningfully higher cost and operational overhead per tenant (more infrastructure to patch, monitor, and scale independently).
Most real SaaS products land on a hybrid model rather than picking one exclusively: the majority of tenants run in the efficient pooled tier, while silo is offered as a tier for tenants with a genuine isolation requirement — commonly a large enterprise customer’s compliance mandate. Forcing every tenant into silo “for safety” is typically not economically viable at scale; forcing every tenant into pooled ignores real, revenue-relevant customer requirements.
Tenant isolation in pooled architectures
This is the highest-scrutiny concern in pooled multi-tenant systems. Relying purely on application-layer WHERE tenant_id = ? filtering means a single missed or incorrectly-scoped query — a bug, a new feature path someone forgot to filter — can leak one tenant’s data into another tenant’s view. This class of bug is easy to introduce and hard to catch in code review, and it is catastrophic for trust in a SaaS product.
Database-layer row-level security, enforced independently of application code correctness, is the defense-in-depth backstop against this failure class. It is not a replacement for careful application code — it is an additional layer that catches what application code gets wrong.
Billing: tenant-level usage attribution
Usage-based or tiered pricing (API calls, storage consumed, active seats) requires knowing which tenant generated which consumption. A monthly aggregate AWS bill provides no basis for invoicing individual tenants. This requires instrumenting usage events with tenant context at the point of consumption, feeding a metering pipeline that is separate from — but often reconciled against — actual AWS cost.
SaaS identity: shared pool vs. per-tenant isolation
A common pattern is a single shared user pool (e.g., one Cognito user pool) carrying tenant context as a custom attribute, rather than fully separate identity providers per tenant. This is usually the right cost/complexity trade-off, but it raises the stakes on testing: every authorization check that scopes access by tenant sits on one shared identity substrate, so a bug in that shared layer risks every tenant simultaneously rather than being contained to one.
Operational visibility: tenant-tagged telemetry
Tenant context propagated through logs, traces, and metrics — not just application data — is what makes incident response tractable in a multi-tenant system. When a deployment goes wrong, “which tenants were actually affected” needs to be answerable in minutes from tagged telemetry, not reconstructed manually after the fact. This requires instrumenting tenant ID consistently through the request path as a design decision made up front.
Pooled vs. Silo Tenancy
| Aspect | Pooled | Silo |
|---|---|---|
| Infrastructure | Shared across tenants | Dedicated per tenant |
| Cost efficiency | High | Low — overhead scales per tenant |
| Isolation guarantee | Depends on software correctness (app + DB-layer controls) | Strong — physical/account-level separation |
| Operational overhead | Lower, centralized | Higher — patch/monitor/scale per tenant |
| Best fit | Majority of tenants, standard requirements | Enterprise customers with compliance/regulatory isolation mandates |
| Typical production model | Default tier | Premium tier layered on top of pooled default (hybrid) |
Common Pitfalls
- Pitfall: Forcing every tenant into a single tenancy model. Why: ignores that isolation requirements vary by customer. Fix: offer a hybrid model — pooled by default, silo available for customers with a genuine isolation requirement.
- Pitfall: Relying solely on application-layer tenant filtering with no database-layer backstop. Why: a single missed or incorrectly-scoped query causes a genuine cross-tenant data leak. Fix: enforce row-level security at the database layer in addition to application-layer filtering.
- Pitfall: Billing metering added as an afterthought, disconnected from actual per-tenant usage. Why: produces inaccurate invoicing or requires manual reconciliation. Fix: instrument tenant-level usage attribution at the point of consumption from the start.
- Pitfall: Shared identity infrastructure with under-tested tenant-scoping logic. Why: a bug here is a single point of failure for every customer’s data isolation. Fix: rigorously test every authorization check that scopes access by tenant.
- Pitfall: No tenant context in telemetry. Why: turns “which customers were affected by this incident” into a slow manual investigation. Fix: propagate tenant ID consistently through logs, traces, and metrics as a design decision made up front.
Interview Questions
- “Design tenant isolation for a pooled multi-tenant database, including what happens if application code has a bug in its tenant filtering.” — tests whether database-layer row-level security is understood as necessary defense-in-depth, not redundant.
- “A large enterprise customer requires dedicated infrastructure for compliance reasons, but the platform is entirely pooled today. How do you accommodate them without re-architecting everything?” — tests whether a hybrid tenancy approach is the instinct.
- “During an incident, leadership asks which customers were affected. Walk through how the architecture answers that.” — tests whether tenant-tagged telemetry was actually designed in, not assumed reconstructable after the fact.
- “Why does usage-based SaaS billing need near-real-time per-tenant tracking rather than monthly aggregate cost?” — tests understanding of tenant-level attribution as a billing requirement, not a nice-to-have.
- “What operational risk does a shared user pool with a tenant_id attribute carry compared to per-tenant identity isolation?” — tests understanding of shared-substrate blast radius versus cost/complexity trade-offs.