Thuta Learning
ProjectsDevOps & Toolsbeginner

Project: Design a Production SaaS Architecture

What you'll walk away with

  • Explain the core ideas behind Project: Design a Production SaaS Architecture
  • Read the diagram and trace how a request or data flows through the architecture
  • Explain what this means for your own project's decisions

Build the mental model

This capstone project doesn't introduce a new tool — it's a worked example of the deployment decision guide the course builds toward: reason out which components are load-bearing and why, rather than starting from a finished diagram.

The scenario: a small subscription SaaS with file uploads, background email sending, and traffic that spikes unpredictably (a product launch, a newsletter mention). Every component below earns its place because of a specific requirement in that sentence.

ComponentWhy it's here
DNSEntry point; resolves the domain to the CDN rather than a server directly.
CDNCaches static assets and terminates TLS close to users, reducing origin load.
Load balancerExists because of the traffic-spike requirement — spreads requests across app instances.
App instancesStateless compute; can scale out horizontally when the load balancer sees more traffic.
DatabaseEvery SaaS needs durable storage for subscription and user data — non-negotiable baseline.
CacheExists because of the traffic-spike requirement — absorbs repeated reads so the database doesn't get hammered.
QueueExists because of the background-email requirement — defers slow work out of the request path.
Object storageExists because of the file-upload requirement — durable home for files outside disposable app instances.
MonitoringNon-negotiable at this scale — one place showing whether every component above is healthy.

Monitoring wraps around everything, because a production system this many components deep needs a single place that shows whether each piece is healthy.

text
PRODUCTION SaaS ARCHITECTURE
----------------------------
[USERS]
   |
   v
[DNS]  (yourapp.com resolves to the CDN)
   |
   v
[CDN]  (caches static assets, terminates TLS at the edge)
   |
   v
[LOAD BALANCER]  (spreads requests across app instances)
   |
   +-------------+-------------+
   v             v             v
[APP #1]      [APP #2]      [APP N]
   |             |             |
   +-------------+-------------+
                 |
   +------------+------------+------------+
   v            v            v            v
[DATABASE]   [CACHE]      [QUEUE]   [OBJECT STORAGE]
(billing,    (Redis:      (email    (uploaded files,
 user data)   hot reads)   jobs)     e.g. S3 bucket)
   |            |            |            |
   +------------+------------+------------+
                      |
                      v
                [MONITORING]
        (metrics, logs, alerts for
         every component above)

Connect it to a real scenario

Baseline: DNS + CDN

Every production app needs these regardless of scenario specifics.

Add load balancer + app instances

Driven by the traffic-spike requirement — a single instance can't survive a spike.

Add database + cache

Database for subscription/user data; cache protects it from repeated-read pressure during a spike.

Add queue only if background work exists

Justified purely by the background-email requirement in scope.

Add object storage only if uploads exist

Justified purely by the file-upload requirement in scope.

Finish with monitoring across every component

Uptime/latency, query performance, queue depth, storage and cache metrics — non-negotiable at this scale.

Notice what didn't get added: nothing here exists because "it's best practice" in the abstract. Each piece traces to one line in the scenario.

Try the working example

javascript
function designArchitecture({ hasFileUploads, hasBackgroundEmail, expectsTrafficSpikes }) {
  const components = [
    {
      name: "Load balancer + multiple app instances",
      needed: expectsTrafficSpikes,
      reason: "distributes traffic across replicas so a spike doesn't overwhelm a single server",
    },
    {
      name: "CDN in front of the app",
      needed: expectsTrafficSpikes,
      reason: "caches static assets at edge locations, cutting origin load during a spike",
    },
    {
      name: "Object storage (e.g. S3-compatible bucket)",
      needed: hasFileUploads,
      reason: "uploaded files need durable storage outside the app servers, which are disposable",
    },
    {
      name: "Background job queue + worker process",
      needed: hasBackgroundEmail,
      reason: "sending email during the request would block the response; a queue defers it",
    },
    {
      name: "Cache layer (e.g. Redis)",
      needed: expectsTrafficSpikes,
      reason: "absorbs repeated reads so the database isn't hit for every request during a spike",
    },
    {
      name: "Managed production database",
      needed: true,
      reason: "every SaaS app needs durable, backed-up storage for user and subscription data",
    },
    {
      name: "Monitoring and alerting",
      needed: true,
      reason: "you need to know about failures before your users report them, spike or not",
    },
  ];

  const chosen = components.filter((c) => c.needed);

  console.log(`Scenario: uploads=${hasFileUploads}, email=${hasBackgroundEmail}, spikes=${expectsTrafficSpikes}`);
  console.log(`Components needed: ${chosen.length} of ${components.length}\n`);
  for (const c of chosen) {
    console.log(`- ${c.name}\n    reason: ${c.reason}`);
  }

  return chosen.map((c) => c.name);
}

designArchitecture({ hasFileUploads: true, hasBackgroundEmail: true, expectsTrafficSpikes: true });
You should see
Scenario: uploads=true, email=true, spikes=true
Components needed: 7 of 7

- Load balancer + multiple app instances
    reason: distributes traffic across replicas so a spike doesn't overwhelm a single server
- CDN in front of the app
    reason: caches static assets at edge locations, cutting origin load during a spike
- Object storage (e.g. S3-compatible bucket)
    reason: uploaded files need durable storage outside the app servers, which are disposable
- Background job queue + worker process
    reason: sending email during the request would block the response; a queue defers it
- Cache layer (e.g. Redis)
    reason: absorbs repeated reads so the database isn't hit for every request during a spike
- Managed production database
    reason: every SaaS app needs durable, backed-up storage for user and subscription data
- Monitoring and alerting
    reason: you need to know about failures before your users report them, spike or not

5-minute try-it

Take the designArchitecture function and re-run it with hasFileUploads: false, hasBackgroundEmail: false, expectsTrafficSpikes: false — a tiny internal tool with steady, predictable traffic. Which components disappear, and does the resulting list still make sense as a real architecture?

One important caution

Adding every component from the full diagram to a small, low-traffic app 'just in case' — a queue or a separate cache layer nobody needs yet, adding cost and operational surface for no real benefit.

Skipping monitoring until after the first production incident, instead of wiring up basic uptime and error alerts from day one.

AWS Well-Architected FrameworkCloud & Deployment

Easy traps

  • Adding every component from the full diagram to a small, low-traffic app 'just in case' — a queue or a separate cache layer nobody needs yet, adding cost and operational surface for no real benefit.
  • Skipping monitoring until after the first production incident, instead of wiring up basic uptime and error alerts from day one.
  • Never assume that working on localhost means it will work in production -- environment, network, database, and security differences can all bite.

Exercise

Take the designArchitecture function and re-run it with hasFileUploads: false, hasBackgroundEmail: false, expectsTrafficSpikes: false — a tiny internal tool with steady, predictable traffic. Which components disappear, and does the resulting list still make sense as a real architecture?

You'll know it worked when: Scenario: uploads=true, email=true, spikes=true Components needed: 7 of 7 - Load balancer + multiple app instances reason: distributes traffic across replicas so a spike doesn't overwhelm a single server - CDN in front of the app reason: caches static assets at edge locations, cutting origin load during a spike - Object storage (e.g. S3-compatible bucket) reason: uploaded files need durable storage outside the app servers, which are disposable - Background job queue + worker process reason: sending email during the request would block the response; a queue defers it - Cache layer (e.g. Redis) reason: absorbs repeated reads so the database isn't hit for every request during a spike - Managed production database reason: every SaaS app needs durable, backed-up storage for user and subscription data - Monitoring and alerting reason: you need to know about failures before your users report them, spike or not

Project: Design a Production SaaS Architecture | Thuta Learning