Skip to content

Chapter 19: SaaS Architecture 101

The foundational architecture decisions that determine your product's scalability ceiling, operational costs, and development velocity for years to come.

Why This Matters

  • Owner: Architecture choices directly impact infrastructure costs, time-to-market, and your ability to serve enterprise customers. A wrong call here can cost 6-12 months of rework.
  • Dev: You will live inside this architecture every day. Understanding the trade-offs lets you advocate for the right patterns and avoid technical debt traps.
  • PM: Architecture constrains what features you can ship and how fast. Knowing the boundaries helps you set realistic roadmaps.
  • Designer: Multi-tenancy models affect data isolation, customization depth, and performance characteristics that shape the user experience.

The Concept (Simple)

Think of SaaS architecture like designing an apartment building:

  • Multi-tenancy = How many families share a building, floors, or rooms
  • API design = The hallways and doors connecting everything
  • Monolith vs Microservices = One big building vs a campus of specialized buildings
  • Event-driven architecture = A mail system where buildings send letters instead of shouting

Every SaaS product must answer: How do we serve many customers from one system without them stepping on each other?


How It Works (Detailed)

Multi-Tenancy Models

Multi-tenancy is the defining characteristic of SaaS. There are three primary models:

┌─────────────────────────────────────────────────────────────────┐
│                    MULTI-TENANCY MODELS                         │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  MODEL 1: Shared Database            (Pool)                     │
│  ┌──────────────────────────────────────────┐                   │
│  │           Single Database                │                   │
│  │  ┌──────┬──────┬──────┬──────┬──────┐   │                   │
│  │  │ id   │tenant│ name │ data │ ...  │   │                   │
│  │  ├──────┼──────┼──────┼──────┼──────┤   │                   │
│  │  │  1   │  A   │ ...  │ ...  │ ...  │   │                   │
│  │  │  2   │  B   │ ...  │ ...  │ ...  │   │                   │
│  │  │  3   │  A   │ ...  │ ...  │ ...  │   │                   │
│  │  └──────┴──────┴──────┴──────┴──────┘   │                   │
│  └──────────────────────────────────────────┘                   │
│  All tenants share tables. Rows filtered by tenant_id.          │
│                                                                 │
│  MODEL 2: Schema-per-Tenant          (Bridge)                   │
│  ┌──────────────────────────────────────────┐                   │
│  │           Single Database                │                   │
│  │  ┌────────────┐  ┌────────────┐          │                   │
│  │  │ Schema: A  │  │ Schema: B  │   ...    │                   │
│  │  │ ┌────────┐ │  │ ┌────────┐ │          │                   │
│  │  │ │ users  │ │  │ │ users  │ │          │                   │
│  │  │ │ orders │ │  │ │ orders │ │          │                   │
│  │  │ └────────┘ │  │ └────────┘ │          │                   │
│  │  └────────────┘  └────────────┘          │                   │
│  └──────────────────────────────────────────┘                   │
│  One DB, separate schemas. Logical isolation.                   │
│                                                                 │
│  MODEL 3: Database-per-Tenant        (Silo)                     │
│  ┌────────────┐  ┌────────────┐  ┌────────────┐                │
│  │   DB: A     │  │   DB: B     │  │   DB: C     │              │
│  │ ┌────────┐  │  │ ┌────────┐  │  │ ┌────────┐  │             │
│  │ │ users  │  │  │ │ users  │  │  │ │ users  │  │             │
│  │ │ orders │  │  │ │ orders │  │  │ │ orders │  │             │
│  │ └────────┘  │  │ └────────┘  │  │ └────────┘  │             │
│  └────────────┘  └────────────┘  └────────────┘                │
│  Complete isolation. Each tenant has own database.              │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Tenancy Model Comparison

CriteriaShared DB (Pool)Schema-per-Tenant (Bridge)DB-per-Tenant (Silo)
Cost per tenantLowestMediumHighest
Data isolationLogical (row-level)Logical (schema-level)Physical
Onboarding speedInstantSecondsMinutes to hours
CustomizationLimitedModerate (schema tweaks)Full
ComplianceHarder (shared tables)ModerateEasiest (full isolation)
Scaling limit~10,000+ tenants~1,000 tenants~100-500 tenants
Migration effortSimple (one DB)ModerateComplex (per-DB)
Noisy neighborHigh riskMedium riskNo risk
Best forSMB / self-serveMid-marketEnterprise / regulated
Backup/restoreAll-or-nothingPer-schema possiblePer-tenant easy

Rule of thumb: Start with Shared DB. Move enterprise customers to DB-per-Tenant when they demand it. Schema-per-Tenant is the middle ground if you use PostgreSQL.


API Design: REST vs GraphQL

Your API is the contract between your frontend, integrations, and potentially third-party developers.

API Style Comparison

AspectRESTGraphQL
Learning curveLowMedium-High
Over-fetchingCommon problemSolved by design
Under-fetchingRequires multiple callsSingle query
CachingHTTP caching built-inRequires custom solutions
VersioningURL or header versioningSchema evolution
File uploadsNative supportRequires workarounds
Real-timeWebhooks / SSESubscriptions built-in
Tooling maturityExcellentGood and growing
Best forPublic APIs, CRUD-heavy appsComplex UIs, mobile apps
REST Endpoint Pattern:
  GET    /api/v1/users          → List users
  POST   /api/v1/users          → Create user
  GET    /api/v1/users/:id      → Get user
  PUT    /api/v1/users/:id      → Update user
  DELETE /api/v1/users/:id      → Delete user

GraphQL Single Endpoint:
  POST   /graphql
  ┌────────────────────────────────┐
  │ query {                        │
  │   user(id: "123") {            │
  │     name                       │
  │     email                      │
  │     orders(last: 5) {          │
  │       total                    │
  │       status                   │
  │     }                          │
  │   }                            │
  │ }                              │
  └────────────────────────────────┘

Recommendation for most SaaS: Start with REST. Add GraphQL later if your frontend team needs flexible querying. Many successful SaaS products never need GraphQL.


Monolith vs Microservices

MONOLITH → MODULAR MONOLITH → MICROSERVICES
Evolution, not revolution

Stage 1: Monolith (0 → PMF)
┌─────────────────────────────────┐
│          Single App             │
│  ┌─────┐ ┌─────┐ ┌──────────┐  │
│  │Auth │ │Bill.│ │ Core     │  │
│  │     │ │     │ │ Product  │  │
│  └─────┘ └─────┘ └──────────┘  │
│         Single Database         │
│  ┌─────────────────────────────┐│
│  │           DB                ││
│  └─────────────────────────────┘│
└─────────────────────────────────┘

Stage 2: Modular Monolith (PMF → Scale)
┌─────────────────────────────────┐
│          Single App             │
│  ┌─────────┐  ┌──────────────┐  │
│  │ Module A │  │  Module B    │  │
│  │ (Auth)   │──│  (Billing)   │  │
│  │ Clear    │  │  Clear       │  │
│  │ boundary │  │  boundary    │  │
│  └─────────┘  └──────────────┘  │
│  Internal APIs between modules  │
└─────────────────────────────────┘

Stage 3: Microservices (Scale → Enterprise)
┌──────────┐   ┌──────────┐   ┌──────────┐
│ Auth     │   │ Billing  │   │ Core     │
│ Service  │──▶│ Service  │──▶│ Product  │
│          │   │          │   │ Service  │
│ Own DB   │   │ Own DB   │   │ Own DB   │
└──────────┘   └──────────┘   └──────────┘
    │               │               │
    ▼               ▼               ▼
┌──────────┐   ┌──────────┐   ┌──────────┐
│  DB: A   │   │  DB: B   │   │  DB: C   │
└──────────┘   └──────────┘   └──────────┘

When to move from monolith to microservices:

  • [ ] Team size exceeds 15-20 engineers
  • [ ] Deployment frequency is bottlenecked by coordination
  • [ ] Specific modules have very different scaling requirements
  • [ ] You need to deploy parts of the system independently
  • [ ] You have proven product-market fit and stable domain boundaries

Critical rule: Premature microservices is one of the top startup killers. A well-structured monolith can serve millions of users. Do NOT split until you feel real pain.


Event-Driven Architecture Basics

Event-driven architecture decouples services by communicating through events rather than direct calls.

SYNCHRONOUS (Request-Response):
  ┌──────────┐  HTTP call  ┌──────────┐  HTTP call  ┌──────────┐
  │ Service A │────────────▶│ Service B │────────────▶│ Service C │
  │           │◀────────────│           │◀────────────│           │
  └──────────┘  response   └──────────┘  response   └──────────┘
  Problem: A waits for B waits for C. Tight coupling.

ASYNCHRONOUS (Event-Driven):
  ┌──────────┐             ┌─────────────────┐
  │ Service A │──publish──▶│  Message Broker  │
  └──────────┘             │  (Kafka / SQS /  │
                           │   RabbitMQ)      │
  ┌──────────┐◀─subscribe──│                  │
  │ Service B │             │                  │
  └──────────┘             │                  │
  ┌──────────┐◀─subscribe──│                  │
  │ Service C │             └─────────────────┘
  └──────────┘
  Benefit: A fires and forgets. B and C process independently.

Common SaaS events:

EventProducersConsumers
user.signed_upAuth serviceEmail, Analytics, Billing
subscription.createdBillingProvisioning, Email, Analytics
invoice.payment_failedBillingEmail, Dunning, Account mgmt
feature.flag_toggledAdminAll services
data.exportedCore productStorage, Notification

In Practice

Real-World Example: Building a Project Management SaaS

Day 1 (Pre-PMF): Monolith with shared database multi-tenancy. REST API. One deploy pipeline. Fast iteration.

Year 1 (Post-PMF): Modular monolith. Extract billing into a clear module. Add a message queue for email notifications. Still shared DB.

Year 2 (Scaling): Extract billing and notification into separate services. Add schema-per-tenant for mid-market customers. Introduce event bus for cross-service communication.

Year 3+ (Enterprise): Offer DB-per-tenant for enterprise. Full microservices for teams that need independent deployment. GraphQL gateway for complex dashboard queries.

Common Mistakes

  1. Starting with microservices -- adds 3-5x complexity with zero benefit pre-PMF
  2. No tenant_id on every table -- retrofit is painful; add it from day one even in shared DB
  3. Building a custom API gateway -- use off-the-shelf (Kong, AWS API Gateway) until you outgrow them
  4. Ignoring API versioning -- your first external integration will break without it
  5. Choosing GraphQL because it is trendy -- REST covers 90% of SaaS use cases with less complexity

Key Takeaways

  • Multi-tenancy model is your most consequential early decision; shared DB is the right default for most startups
  • REST is the safe, productive choice for SaaS APIs; add GraphQL only when frontend complexity demands it
  • Start as a monolith, evolve to modular monolith, extract services only when team size or scaling forces it
  • Event-driven patterns decouple services and enable reliable async workflows -- introduce them incrementally
  • Every table needs a tenant_id column from day one, no exceptions
  • Architecture should evolve with your business stage, not your ambitions

Action Items

Owner

  • [ ] Decide your initial target market (SMB vs enterprise) -- this drives tenancy model choice
  • [ ] Budget for infrastructure that matches your tenancy model
  • [ ] Understand that architecture rework is normal at each growth stage

Dev

  • [ ] Add tenant_id to every table and enforce row-level filtering in your ORM/query layer
  • [ ] Design your monolith with clear module boundaries from the start
  • [ ] Set up a message queue early for email/notification workflows
  • [ ] Document your API with OpenAPI/Swagger from day one

PM

Designer

  • [ ] Design tenant-switching UIs for users who belong to multiple organizations
  • [ ] Plan for tenant-specific branding and customization within your design system
  • [ ] Consider data isolation when designing admin dashboards and reporting views

The Product Builder's Playbook