Appearance
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
| Criteria | Shared DB (Pool) | Schema-per-Tenant (Bridge) | DB-per-Tenant (Silo) |
|---|---|---|---|
| Cost per tenant | Lowest | Medium | Highest |
| Data isolation | Logical (row-level) | Logical (schema-level) | Physical |
| Onboarding speed | Instant | Seconds | Minutes to hours |
| Customization | Limited | Moderate (schema tweaks) | Full |
| Compliance | Harder (shared tables) | Moderate | Easiest (full isolation) |
| Scaling limit | ~10,000+ tenants | ~1,000 tenants | ~100-500 tenants |
| Migration effort | Simple (one DB) | Moderate | Complex (per-DB) |
| Noisy neighbor | High risk | Medium risk | No risk |
| Best for | SMB / self-serve | Mid-market | Enterprise / regulated |
| Backup/restore | All-or-nothing | Per-schema possible | Per-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
| Aspect | REST | GraphQL |
|---|---|---|
| Learning curve | Low | Medium-High |
| Over-fetching | Common problem | Solved by design |
| Under-fetching | Requires multiple calls | Single query |
| Caching | HTTP caching built-in | Requires custom solutions |
| Versioning | URL or header versioning | Schema evolution |
| File uploads | Native support | Requires workarounds |
| Real-time | Webhooks / SSE | Subscriptions built-in |
| Tooling maturity | Excellent | Good and growing |
| Best for | Public APIs, CRUD-heavy apps | Complex 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:
| Event | Producers | Consumers |
|---|---|---|
user.signed_up | Auth service | Email, Analytics, Billing |
subscription.created | Billing | Provisioning, Email, Analytics |
invoice.payment_failed | Billing | Email, Dunning, Account mgmt |
feature.flag_toggled | Admin | All services |
data.exported | Core product | Storage, 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
- Starting with microservices -- adds 3-5x complexity with zero benefit pre-PMF
- No tenant_id on every table -- retrofit is painful; add it from day one even in shared DB
- Building a custom API gateway -- use off-the-shelf (Kong, AWS API Gateway) until you outgrow them
- Ignoring API versioning -- your first external integration will break without it
- 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_idcolumn 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_idto 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
- [ ] Map feature requirements to architecture constraints (see Chapter 20: Tech Stack Decisions)
- [ ] Plan for multi-tenancy requirements in your PRDs
- [ ] Understand deployment implications before committing to release dates (see Chapter 21: Shipping and Deployment)
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