How I Built a Multi-Tenant SaaS Platform on AWS
A deep dive to provide valuable insights into the decisions, challenges, and solutions that you may need to consider for your B2B (multi-tenant) platform.
Introduction
Building a scalable, secure, and flexible SaaS platform is not a small task. In this article, I'll walk you through how I architected and built CONSEN.CO, a sophisticated customer application intake and onboarding platform for SMEs across various industries.
Please Note: We’re not affiliated with AWS in anyways. This article is just to explain how we leveraged this Cloud Provider in building our platform.
Whether you're considering building your own SaaS solution or just curious about modern cloud architecture, this deep dive will provide valuable insights into the decisions, challenges, and solutions that shaped our platform.
The Business Problem
Many businesses struggle with customer onboarding. Whether it's loan applications for financial institutions, patient intake for healthcare providers, or job applications for HR departments, the process is often fragmented, manual, and frustrating for both businesses and their customers.
CONSEN was born to solve this problem: a flexible, industry-agnostic platform that allows businesses to create custom application workflows, collect information through dynamic forms, process documents, and seamlessly onboard new customers—all while maintaining compliance and security standards.
Architectural Decisions
Multi-Tenancy Model
One of the first and most critical decisions was how to implement multi-tenancy. We needed to support both:
Pooled resources: Where multiple tenants share infrastructure (more cost-effective)
Dedicated resources: For enterprise clients with specific compliance or performance needs
We implemented a hybrid approach resembling AWS's account structure and resource tagging:
├── Master Account (Billing, IAM)
│ ├── Shared Services Account
│ │ └── Common resources (CloudFront, Route53, etc.)
│ ├── Pooled Tenant Account
│ │ └── Resources tagged by tenant-id
│ └── Dedicated Tenant Accounts (for enterprise clients)
└── Tenant-specific resources
This architecture allows us to maintain separation where needed while optimizing costs for smaller clients.
Serverless First
We embraced serverless architecture to maximize scalability and minimize operational overhead:
AWS Lambda for compute (using both Python and Node.js)
API Gateway for RESTful APIs
DynamoDB for database needs
S3 for file storage
CloudFront for content delivery
This approach allows us to scale from zero to thousands of users without managing servers, while paying only for what we use.
Core Technical Components
Infrastructure as Code
All infrastructure is defined using CloudFormation templates, divided into two main categories:
Bootstrap Template: Sets up the core infrastructure needed for all tenants
Tenant Templates: Provisions tenant-specific resources
This approach ensures consistency and allows for rapid tenant onboarding. A new tenant can be fully provisioned in minutes rather than days.
Authentication and Authorization
Security is paramount in a multi-tenant system. We implemented:
AWS Cognito for user authentication with custom user pools per tenant
Custom Authorizers in API Gateway to validate JWT tokens and enforce tenant isolation
Fine-grained IAM policies to ensure tenants can only access their own resources
Dynamic Form Builder
At the heart of CONSEN is our dynamic form system built with:
React and Material-UI for the frontend
react-jsonschema-form for form rendering and validation
DynamoDB to store form definitions as JSON schemas
This allows tenants to create complex forms without writing code:
// Example of a form schema stored in DynamoDB
{
"title": "Loan Application",
"type": "object",
"required": ["fullName", "income"],
"properties": {
"fullName": {
"type": "string",
"title": "Full Name"
},
"income": {
"type": "number",
"title": "Annual Income"
},
// More fields...
}
}
Visual Workflow Builder
To model complex business processes, we built a visual workflow editor using react-flow. This allows tenants to:
Define multi-step application processes
Add conditional logic and branching
Configure integrations with external systems
Set up automated notifications
Each workflow consists of nodes representing different actions:
START → T&C → APPLICATION → REVIEW → DECISION → END
↓
NOTIFY
Workflows are stored as directed graphs in DynamoDB and executed by our workflow engine.
Document Management
For document handling, we implemented:
S3 for secure storage with lifecycle policies
Lambda functions for document processing and generation
CloudFront for secure delivery of documents to authorized users
Data Modeling in DynamoDB
DynamoDB's flexible schema was perfect for our multi-tenant data model. Here's how we structured our main tables:
Tenant Table
PK: TENANT#tenant-id
SK: METADATA
Attributes: name, industry, settings, etc.
Offering Table
PK: TENANT#tenant-id
SK: OFFERING#offering-id
Attributes: name, description, category, workflowId, etc.
Workflow Table
PK: TENANT#tenant-id
SK: WORKFLOW#workflow-id
Attributes: name, nodes, edges, etc.
Submission Table
PK: TENANT#tenant-id
SK: SUBMISSION#submission-id
GSI1-PK: CUSTOMER#customer-id
Attributes: offeringId, status, submittedData, etc.
We used sparse indexes and composite keys to enable efficient queries while maintaining tenant isolation.
Handling Asynchronous Operations
Many operations in CONSEN are long-running (document generation, integrations with external systems). We implemented an event-driven architecture using:
SNS for publishing events
SQS for reliable message processing
Lambda functions as event consumers
This pattern ensures reliability and allows for retry mechanisms on failures.
Challenges and Solutions
Challenge 1: Tenant Isolation
Solution: We implemented a custom authorizer that validates the JWT token from Cognito and extracts the tenant ID. This ID is then used to scope all database queries and S3 operations, ensuring tenants can only access their own data.
Challenge 2: Database Performance
Solution: We carefully designed our access patterns and used GSIs (Global Secondary Indexes) to support efficient queries. For read-heavy operations, we implemented a caching layer using ElastiCache.
Challenge 3: File Security
Solution: We implemented pre-signed URLs with short expiration times for file uploads and downloads. Additionally, we used S3 bucket policies and CloudFront signed URLs to prevent unauthorized access.
Lessons Learned
Start with clear multi-tenant boundaries: Designing for tenant isolation from day one saved us from painful refactoring later.
Embrace infrastructure as code: Our CloudFormation templates made tenant onboarding and updates consistent and reliable.
Design for extensibility: Using JSON schemas for form definitions and a component-based architecture for workflows allowed for incredible flexibility.
Monitor and optimize costs: Serverless isn't always cheaper. We had to carefully monitor usage patterns and optimize our Lambda functions to keep costs predictable.
Invest in developer experience: Building good local development and testing tools paid dividends in productivity.
Conclusion
Building CONSEN taught me that creating a multi-tenant SaaS platform on AWS requires careful planning, a deep understanding of cloud services, and a focus on security and scalability. The serverless approach allowed us to build a solution that scales with our customers' needs while keeping operational overhead low.
The combination of dynamic forms, visual workflows, and document management capabilities has enabled our customers to digitize and streamline their application processes, reducing onboarding times from weeks to days or even hours.
If you're embarking on a similar journey, I hope these insights help you navigate the challenges of building your own SaaS platform. Feel free to reach out with questions or share your own experiences in the comments!