Project plan
This repo rebuilds the AWS Serverless SaaS Reference Solution from scratch, one small step at a time. Each step is one pull request with its own doc in docs/, and each step is deployed to AWS and verified before the PR is opened.
What we are building
Section titled “What we are building”A multi-tenant SaaS app (products and orders) where many customer companies (“tenants”) share one deployment, and each tenant can only ever see its own data.
Key ideas the reference solution teaches, and the step that introduces each one:
| Idea | Step |
|---|---|
| Control plane vs application plane | 01, 08 |
| Tiers (Basic / Standard / Premium = pooled, Platinum = silo) and API keys / usage plans per tier | 02, 04, 09 |
| Tenant identity: tenant id + role + tier as custom Cognito claims | 03 |
| Lambda authorizer that validates the JWT and vends tenant-scoped STS credentials | 04 |
Data isolation in a shared (pooled) DynamoDB table using dynamodb:LeadingKeys |
05 |
| Tenant-aware logging, metrics and tracing hidden in a Lambda layer | 04, 05 |
| Tenant user management with role-based rules | 07 |
| Automated onboarding/offboarding driven by events | 08 |
| Silo deployment for premium customers | 09 |
| Admin and tenant web apps | 10, 11 |
| Rolling out code changes to every tenant stack | 12 |
Steps (one PR each)
Section titled “Steps (one PR each)”| # | Branch | What it adds | Deploys |
|---|---|---|---|
| 01 | step-01-control-plane |
CDK app scaffold + ControlPlaneStack built with the SaaS Builder Toolkit (SBT): system-admin Cognito pool, control plane API, EventBridge bus |
yes |
| 02 | step-02-bootstrap |
BootstrapStack: tenant→stack mapping table, one API key per tier stored in SSM |
yes |
| 03 | step-03-tenant-identity |
TenantTemplateStack (pooled): tenant Cognito user pool with tenantId / userRole / tenantTier attributes |
yes |
| 04 | step-04-api-authorizer |
Shared Python Lambda layer, tenant authorizer, REST API, usage plans per tier, /whoami debug endpoint, unit tests |
yes |
| 05 | step-05-product-service |
Product microservice (5 Lambdas + pooled table) with tenant isolation via scoped credentials | yes |
| 06 | step-06-order-service |
Order microservice | yes |
| 07 | step-07-user-management |
Tenant user management service (TenantAdmin / TenantUser rules) | yes |
| 08 | step-08-onboarding |
SBT core application plane: provisioning + deprovisioning script jobs; onboard a pooled tenant end to end | yes |
| 09 | step-09-silo-tier |
Platinum tier: a dedicated tenant stack per tenant | yes |
| 10 | step-10-admin-app |
S3 + CloudFront hosting construct and the React admin app (system admin) | yes |
| 11 | step-11-saas-app |
React SaaS app (tenant login, products, orders, users) | yes |
| 12 | step-12-tenant-pipeline |
CodePipeline + Step Functions that updates every tenant stack in waves | yes |
How the PRs work
Section titled “How the PRs work”Each step branches from main, is deployed and verified, then opened as a PR to main and merged with a merge commit. Each PR’s diff is exactly that step, so you can review it at any time, even after it’s merged.
Differences from the original (on purpose)
Section titled “Differences from the original (on purpose)”- React (Vite + TypeScript) instead of Angular for both web apps.
- Latest versions: SBT
0.9.x(the original pins0.3.9, whose API differs), Python 3.13 Lambdas on ARM64, Powertools for AWS Lambda v3, and PyJWT instead of python-jose. - A small
/whoamiendpoint in step 04 so the authorizer can be tested before any business service exists. - Lambda Insights and the commented-out canary deployment settings are left out.
Prerequisites
Section titled “Prerequisites”- AWS account with admin credentials configured (
aws sts get-caller-identityworks) - Node.js 20+, Python 3.12+, AWS CDK CLI (
npx cdk) - Docker running (CDK uses it to bundle Python Lambdas)