DEV Community

Feng Zhang
Feng Zhang

Posted on Originally published at prachub.com

Secure Multitenant SaaS Architecture Explained — Tech Interview Concept (2026)

Multitenant SaaS design questions are easy to answer badly.

A weak answer says, "Add tenant_id everywhere and encrypt the database." A better answer explains where tenant isolation is enforced, how authorization works across each access path, how background jobs carry tenant context, and how you stop one customer from hurting another customer's availability.

This topic comes up often in system design interviews, especially for enterprise products with sensitive data. The original PracHub concept write-up on secure multitenant SaaS architecture frames it around legal workflows, where documents, matters, privileged communications, and audit trails all raise the bar.

Let's turn that into a practical interview-ready design.

What the interviewer is really testing

The interviewer wants to know if you can design a SaaS system where many customers share infrastructure without sharing:

  • Data
  • Permissions
  • Search results
  • Cached responses
  • Background jobs
  • Operational blast radius
  • Observability access

That last point matters more than many candidates expect. Multitenancy is not just a database problem. It touches authentication, authorization, queues, object storage, search indexes, logs, metrics, admin tools, and incident response.

A good answer should show that you understand tradeoffs. Shared infrastructure is cheaper and simpler early on. Dedicated infrastructure gives stronger isolation for large or regulated customers, but it adds migration, deployment, cost, and operations work.

Pick the tenancy model first

Most SaaS systems use one of three models:

  1. Shared database, shared schema

Every tenant's data lives in the same tables. Tenant-owned tables include a tenant_id.

This is cost-effective and simpler to operate, but it depends on correct tenant scoping everywhere.

  1. Shared database, separate schema

Each tenant has its own schema inside the same database.

This gives a stronger boundary than shared tables, but migrations and schema management become harder.

  1. Separate database per tenant

Each tenant has its own database.

This improves isolation and noisy-neighbor control, but connection management, migrations, backups, and provisioning are more complex.

For most interview answers, start with shared services and a shared Postgres database with strong logical isolation. Then mention that large or regulated tenants can move to a dedicated database, bucket, or deployment tier.

That tradeoff sounds realistic. It avoids pretending every customer gets fully dedicated infrastructure from day one.

Tenant isolation must exist at multiple layers

Do not rely on one WHERE tenant_id = ? check and call it done.

A serious design applies tenant scoping across the system:

  • API authentication
  • Authorization middleware
  • Database predicates
  • Postgres row-level security
  • Object storage key prefixes
  • Signed URL generation
  • Search and vector index filters
  • Cache key prefixes
  • Queue routing
  • Worker pools
  • Audit logs
  • Admin tools
  • Observability permissions

For example, a document might be stored in S3 under:

tenant/{tenant_id}/matter/{matter_id}/doc/{doc_id}
Enter fullscreen mode Exit fullscreen mode

That naming pattern helps, but naming alone is not security. Your service should authorize the request before it generates a signed URL. The signed URL should have a short TTL. Metadata should be checked before bytes are served.

Authentication and authorization are different

Authentication answers: "Who are you?"

Authorization answers: "What can you access?"

Enterprise SaaS often supports SAML or OIDC single sign-on. The system maps identity-provider groups into application roles. Session tokens or JWTs may contain claims such as:

{
  "sub": "user_123",
  "org_id": "tenant_456",
  "roles": ["admin"],
  "exp": 1760000000
}
Enter fullscreen mode Exit fullscreen mode

Roles are useful, but roles alone are usually too coarse.

RBAC works for broad permissions like:

  • admin
  • member
  • viewer

Legal-style workflows often need ABAC too. Access may depend on:

  • tenant_id
  • matter_id
  • document_classification
  • jurisdiction
  • ethical_wall_group

A common pattern is to combine them. Roles grant capabilities. Attributes constrain which resources those capabilities apply to.

For example, a user may have permission to view documents, but only for matters they belong to, and only if the document is not blocked by an ethical wall.

Put authorization near every resource access

Route-level checks are not enough.

You need a common authorization API, something like:

authorize(actor, action, resource)
Enter fullscreen mode Exit fullscreen mode

That call should happen before sensitive resource access, whether the caller is:

  • A REST endpoint
  • A GraphQL resolver
  • A background worker
  • A CSV export job
  • A search endpoint
  • A document preview service
  • An admin impersonation tool

Policy can live in application code or a system like Open Policy Agent. The key idea is consistency. Every path that reads or writes tenant data needs a policy decision.

This is where many designs fail. The main API may be scoped correctly, while a batch export, preview endpoint, webhook retry, or support tool bypasses the same checks.

Design the database so unsafe queries are harder to write

In a shared-schema model, every tenant-owned table should include tenant_id.

Example tables:

documents (
  tenant_id,
  matter_id,
  document_id,
  title,
  created_at
)

matters (
  tenant_id,
  matter_id,
  name,
  created_at
)
Enter fullscreen mode Exit fullscreen mode

Indexes should match tenant-scoped access patterns:

CREATE INDEX documents_tenant_matter_created_idx
ON documents (tenant_id, matter_id, created_at);
Enter fullscreen mode Exit fullscreen mode

Business identifiers should usually be unique within a tenant, not globally:

UNIQUE (tenant_id, external_id)
Enter fullscreen mode Exit fullscreen mode

Postgres row-level security can backstop application mistakes:

tenant_id = current_setting('app.tenant_id')
Enter fullscreen mode Exit fullscreen mode

RLS is not a replacement for clean application design, but it can reduce the damage from an unscoped query.

Treat search and vector retrieval as high-risk paths

Search is a common leak point.

If you use OpenSearch, Elasticsearch, pgvector, or a vector database, every query must include tenant and permission filters before results are returned.

For sensitive documents, retrieval should filter by:

  • tenant_id
  • matter_id
  • User-accessible document IDs

Do not fetch the top-k chunks globally and then filter afterward. That can leak through ranking, snippets, timing, logs, or accidental response fields. The permission boundary needs to be part of retrieval, not a cleanup step after retrieval.

Derived data needs the same treatment. OCR text, embeddings, summaries, previews, and cached snippets are still tenant data.

Background jobs need explicit tenant context

Asynchronous processing is another common source of bugs.

Suppose a user uploads a legal document. The system creates jobs for virus scanning, OCR, embedding generation, indexing, and preview creation.

Each job should carry tenant context explicitly:

{
  "tenant_id": "tenant_456",
  "matter_id": "matter_789",
  "document_id": "doc_123",
  "requested_by": "user_999"
}
Enter fullscreen mode Exit fullscreen mode

The system should validate permissions when the job is enqueued and again when it runs. That second check matters because permissions can change while a job is waiting.

Derived artifacts should be written back into tenant-scoped stores. Queue names, routing keys, and worker pools may also be partitioned or rate-limited by tenant.

Encryption helps, but it does not fix bad authorization

Encryption belongs in the design:

  • TLS for data in transit
  • Storage encryption for data at rest
  • Optional per-tenant keys through AWS KMS, GCP KMS, or HashiCorp Vault

Per-tenant envelope encryption can support tenant-specific key rotation or deletion. It also adds latency, key-management paths, and new failure modes.

The interview mistake is to over-index on encryption. Encryption protects against storage compromise. It does not stop an authenticated user from reading the wrong matter if authorization is broken.

Access-control correctness is the bigger application-layer risk.

Audit logs should answer who did what

Enterprise systems need audit trails for security and compliance.

Log events such as:

  • Login
  • SSO group sync
  • Permission changes
  • Document upload
  • Document download
  • Search
  • Export
  • Admin impersonation
  • Failed authorization

A useful audit event includes:

{
  "actor_id": "user_999",
  "tenant_id": "tenant_456",
  "resource_id": "doc_123",
  "action": "document.download",
  "decision": "allowed",
  "ip": "203.0.113.10",
  "user_agent": "Mozilla/5.0",
  "timestamp": "2026-01-15T12:00:00Z"
}
Enter fullscreen mode Exit fullscreen mode

The audit log should avoid document contents, prompts, secrets, and access tokens.

There is also a design tradeoff. Synchronous audit writes give stronger confidence but add latency and failure coupling. Asynchronous audit writes improve availability but need durable queues and retries. A strong answer proposes a hybrid: block on authorization, emit audit events to a durable append-only stream, and monitor for delayed or missing audit records.

Protect tenants from noisy neighbors

Security includes availability.

One tenant's large import should not exhaust all background workers or saturate shared database IOPS for everyone else.

Use:

  • Per-tenant rate limits
  • Quotas
  • Queue partitioning
  • Worker-pool isolation
  • Query timeouts
  • Resource monitoring by tenant

This is where shared infrastructure needs careful guardrails. Tenant isolation includes performance failure boundaries, not just data leakage.

A strong interview answer structure

If asked to design a secure multitenant document management system, structure the answer like this:

  1. Clarify requirements

Ask whether tenants are companies or law firms, whether users can belong to multiple tenants, whether documents are scoped to matters, and whether the system needs SSO, audit logs, data residency, or dedicated infrastructure.

  1. State assumptions

Use shared application services, shared Postgres for metadata, object storage for files, and strict logical isolation by tenant_id and matter_id.

  1. Cover identity and access

Use OIDC or SAML, map groups to roles, combine RBAC and ABAC, and call authorize(actor, action, resource) across all access paths.

  1. Cover data isolation

Put tenant_id on tenant-owned tables, use scoped indexes, enable RLS on sensitive tables, namespace object keys, and generate signed URLs only after authorization.

  1. Cover operations

Add audit logs, per-tenant rate limits, encrypted storage, key management, queue partitioning, and guardrails for admin access.

  1. Call out tradeoffs

Shared database is simpler and cheaper for most tenants. Dedicated databases or buckets may be needed for large or regulated customers.

If you want more interview prompts around this style of system design, PracHub has a broader set of technical interview questions that pair well with this topic.

Common mistakes to avoid

The biggest mistake is treating tenant_id like a UI filter. It is a security boundary.

Another mistake is checking authorization only at the route layer. Secondary paths often cause leaks: exports, previews, search snippets, OCR jobs, embeddings, cached responses, webhook retries, and support tooling.

A third mistake is using encryption as a substitute for authorization. You need both, but they solve different problems.

If you can explain those risks clearly, your answer will sound much closer to production engineering than checklist security. For a compact version of the concept, use the PracHub guide to secure multitenant SaaS architecture as a review sheet before practicing the full system design.

Top comments (0)