Skip to main content
ANVISoftware Solutions

Multi-Tenant SaaS Application

Professional50-70 hours

The hardest project here. Serve several customers from one system without ever leaking data between them.

The brief

What the finished thing needs to do.

  • Multiple tenant organisations, each with their own users and data
  • Complete data isolation between tenants, enforced structurally
  • Per-tenant configuration and feature flags
  • Tenant-aware authentication and authorization
  • Usage metering per tenant
  • Onboarding a new tenant without a deployment

How to structure it

The central decision is the isolation strategy: a database per tenant, a schema per tenant, or a shared schema with a tenant identifier on every row. Each has genuine trade-offs in isolation strength, operational cost and query complexity. Choose deliberately and write down why.

Whichever you pick, isolation must be enforced structurally rather than by remembering to filter. A forgotten WHERE clause in a shared-schema design is a data breach, so the filter has to be applied somewhere it cannot be omitted — a global query filter, or a per-request scoped context.

Tenant resolution happens once per request, early, and everything downstream reads it from there. Passing a tenant identifier through method signatures manually is exactly how one call site gets missed.

Test isolation explicitly and adversarially: authenticate as tenant A and attempt to reach tenant B's records by direct identifier. That test is not optional.

Why these technologies

Global query filters
Applies the tenant filter automatically so it cannot be forgotten on an individual query.
Scoped tenant context per request
Resolved once at the boundary and available everywhere without manual passing.
Per-tenant configuration store
Onboarding should be data, not a code change and deployment.
Adversarial isolation tests
Cross-tenant leakage is the one failure that cannot be allowed to reach production.

Build it in this order

Each stage produces something that works. That matters — a project that only runs at the very end is a project people abandon.

  1. Choose and document the isolation strategy

    Write down the trade-offs and the decision before writing code.

  2. Tenant model and resolution

    Identify the tenant from the request and make it available for the request's lifetime.

  3. Structural isolation

    Apply filtering where it cannot be bypassed, not at each call site.

  4. Adversarial isolation tests

    Attempt cross-tenant access by direct identifier and confirm it fails.

  5. Tenant-aware identity

    Users belong to a tenant; a token is valid only for its own tenant.

  6. Per-tenant configuration

    Settings and feature flags resolved per tenant at run time.

  7. Usage metering

    Record usage per tenant without adding meaningful request latency.

  8. Self-service onboarding

    Create a tenant through the application, with no deployment required.

Done means

How to know it is finished

Check each of these before moving on. If one fails, the project is not done yet — and that is useful information rather than a setback.

  • No request can return another tenant's data, including by guessing identifiers
  • Isolation is enforced structurally, not by per-query discipline
  • Adversarial cross-tenant tests exist and pass
  • A new tenant can be onboarded without a code change or deployment
  • Feature flags and configuration resolve per tenant correctly
  • Usage is metered accurately per tenant

If you want to go further

Extensions worth attempting

Only once the core build meets every criterion above.

  • Add per-tenant data export and deletion for compliance requests
  • Add tenant-level rate limiting
  • Support migrating a large tenant to a dedicated database
  • Add per-tenant custom domains

Have a project worth talking through?

Tell us what you're building or what's slowing your current system down. We'll give you a direct read on scope and approach.