Deploying Multi-Tenant SaaS Application on AWS

08 Jun 2023 Konstantin Maevskiy

Discover the scalable multi-tenant backend architecture and robust CI/CD process at the heart of the client-server solution we developed for our major pharmaceutical customer. Initially crafted to assist asthma patients with medication adherence, this app also enables doctors to monitor treatment progress remotely. The market success of this solution led our customer to strategically scale it, offering it to other healthcare providers under a white labeling model. To technically achieve this strategy, we needed to develop a scalable, distributed backend infrastructure that was both user-friendly and cost-effective for supporting numerous individual client-server application instances, each dedicated to a specific partner. This also necessitated establishing a robust CI/CD process. Join us on this journey through the technical solutions that have been key to our customer’s success.

Multi-Tenant Architecture for SaaS Application

Let’s begin by defining the tasks that stand before us in terms of backend infrastructure. Our customer is shipping their own SaaS (Software as a Service) application, as well as numerous applications belonging to their partners. Accordingly, each application requires a separate backend infrastructure. The cloud of choice had been AWS from the early days of the project. To enhance the convenience and flexibility of servicing our customer’s numerous partners, we create and manage individual AWS accounts for each one. Within these accounts, we deploy the infrastructure for each partner’s applications, thereby ensuring efficient resource segregation and management.

In our multi-tenant architecture, a unified API serves as the central entry point for all users of our partners. The data for each partner is isolated at the database level within their respective AWS accounts. We utilize a Main Configuration Database to store the connection string information for the databases of all partners. Each end user is assigned an ID that indicates their affiliation with a specific partner. When a user’s request arrives at the unified API, the ConnectionManager (Data Access Layer) identifies the user’s associated partner using this ID and, accordingly, directs the request to the appropriate database, providing the relevant connection string.

Multi-Tenant Architecture Components

Let’s examine each component of the backend infrastructure and its functional role. The journey of each user’s request begins at the doorstep of Amazon Route 53, the domain name system that is the backbone of domain management, ensuring high availability and seamless failover mechanisms. Route 53’s role extends beyond that of a simple DNS service; it plays a crucial role in managing domain names for HTTPS and SSL certificates, thus providing a secure and reliable pathway for data. This functionality is essential for maintaining the integrity and security of internet communications.

Click or tap on the picture to open it in full size

Encapsulating all subsequent components is the Virtual Private Cloud (VPC), a cornerstone of AWS that provides a customizable isolated network space, complete with subnets, route tables, and network gateways. It ensures that the resources and communications within it remain private and unreachable by unauthorized users.

As the request travels deeper, it encounters AWS Shield and the AWS Web Application Firewall (WAF). These two work in tandem as guardians of the realm. AWS Shield provides protection against Distributed Denial of Service (DDoS) attacks, shielding applications from malicious traffic. The WAF, on the other hand, stands as a filter, scrutinizing incoming traffic against common web exploits and providing rules that control traffic based on conditions such as IP addresses, HTTP headers, and body contents.

The road then forks; to the right, we have the Application Load Balancer, acting as a sophisticated router, intelligently directing queries to their destined endpoints within the specific partner’s AWS account. The Application Load Balancer evenly distributes incoming application traffic across multiple targets, such as ECS containers. In this case, it directs traffic to the correct .NET-based API application running in Amazon ECS:

Caregiver API: Tailored for those overseeing patient care, facilitating monitoring and communication.

Doctor API: A portal for doctors to access, evaluate, and action on patient health data.

Mobile API: This API serves as the bridge for mobile applications, handling requests and interactions from both patients and caregivers.

Admin API: The administrative command center, overseeing the ecosystem’s operation.

SignalRCore API: The real-time messenger, ensuring instant updates and notifications.

For staging environments, the API applications are hosted on a more cost-effective Amazon EC2 instance instead of the pricier Amazon ECS. These APIs are the backbone of the application’s architecture, facilitating a seamless and secure exchange of data. They bridge the user interfaces with a sophisticated MS SQL Server Database hosted on Amazon EC2, which excels in handling complex, transaction-intensive operations. Alongside, they also interface with Amazon ElastiCache for Redis, which is crucial for replicating signals in our WebSockets operating in a web-farm mode with multiple replicas. The functionality of ElastiCache for Redis, with its lightning-fast response times and exceptional scalability, ensures efficient data operations.

In the branch to the left, Amazon CloudFront, our global CDN, accelerates content delivery by caching content at edge locations closest to the users, reducing latency and improving load times. CloudFront interacts with Amazon S3, where it stores web assets and serves webpages for Caregiver’s, Doctor’s, and Admin’s Web portals. Additionally, it serves as a public access layer to the private S3 bucket. From here, these websites communicate back with the Application Load Balancer.

AWS Lambda, a serverless computing service, efficiently interfaces with the API through the Application Load Balancer. This serverless worker service is vital for generating and sending reminders and for creating medication schedules based on predetermined times. For instance, if a patient is scheduled to take medication at a specific time, they receive a notification 15 minutes prior as a reminder. If the medication is not taken, another reminder follows 10 minutes later. Lambda continuously monitors these interactions, calculates statistics on medication adherence, and executes other related tasks, ensuring that patients consistently follow their prescribed medication schedules.

The Private Subnet functions as a secluded area within the broader Virtual Private Cloud (VPC). This subnet is specifically designed for resources that do not require direct access from the public internet, providing an additional layer of security.

Having explored the intricacies of our backend architecture, let’s pivot our focus to the implementation of the Continuous Integration and Continuous Deployment (CI/CD) process. This vital element in our development cycle ensures that new features, updates, and bug fixes are seamlessly integrated and promptly deployed, maintaining the high performance and reliability of our backend systems. Let’s examine how this process is orchestrated and the impact it has on our overall backend management.

CI/CD Pipeline For Multi-Tenant Architecture

The Continuous Integration/Continuous Deployment pipeline is the heartbeat of our delivery system, ensuring that new features are integrated, built, tested, and deployed with precision and efficiency. Our customer has established a General AWS account that orchestrates the CI/CD infrastructure, enabling the delivery and deployment of release builds into separate AWS accounts crafted for each of their partners. In each of these separate AWS accounts, a custom application designed for a specific partner operates actively, continually receiving and benefiting from the latest updates and feature enhancements.

Let’s walk through the main stages of the CI/CD process:

1. Build App: When a new feature breathes its first byte in the Stage environment, the CI/CD pipeline springs into action. It pulls the latest Stage branch code from GIT, constructs a build comprising a folder filled with artifacts, and compresses it into an archive.

Click or tap on the picture to open it in full size

2. Upload: This archived build file is then lifted to S3 in the General AWS account. In parallel, CI/CD fetches the Deployment YAML configurations from the Git repository, pertinent to the Stage branch code, and places them in the Redis database, also nestled in the General AWS account. YAML, for those unfamiliar, stands for Yet Another Markup Language. It’s a human-friendly format that is especially popular in DevOps for its clarity and simplicity, using text to represent data in a key-value pairing that’s easy for both computers and people to read and understand.

3. Deploy: A symphony of Publish/Subscribe interactions plays out between the Redis repository and our custom Deployer. Upon receiving a Publish signal, the Deployer knows it’s showtime. It retrieves the artifacts from S3 and, with the help of encrypted YAML configurations stored in AWS KMS, swaps placeholder variables in the code with real values, such as connection strings and other secure elements. This method ensures that the actual values remain a mystery in the source code, bolstering the application’s security. The Deployer then crafts a Docker Image, configured with the now-secured application, and deploys it onto IIS, which stands for Internet Information Services—a .NET web server that hosts websites, APIs, and the like. In production, this lives within a container in AWS ECS of the partner’s account, while for staging purposes, it resides in EC2 to economize resources.

A word about our custom Deployer: It’s a masterstroke of our DevOps team, a generic deployment solution that bends to the specifics of any environment. At its core is our custom builder, leveraging the powers of Docker and Firebase to encapsulate all the necessary features for a successful deployment across varied landscapes.

4. Validation: After deployment, the system doesn’t just take a breath and relax. It conducts a series of automated QA API tests built on Postman collections, ensuring the new feature’s workability through smoke API testing.

We utilize Active Directory for robust authentication and authorization of users and services that interact with our MS SQL Server Database on EC2 in the production AWS account. Leveraging Role-Based Access Control within Active Directory, we ensure that precise permissions are aligned with the roles in our pipeline, granting appropriate access levels for deploying and managing database changes.VPN Peering is meticulously configured, allowing the General AWS account’s resources to engage in a seamless dance with the resources of the specific partner’s AWS accounts.

Click or tap on the picture to open it in full size

For each customer’s partner, we typically roll out Stage, Pre-production, and Production environments. However, over time, to conserve resources and focus, we often whittle it down to just Pre-production and Production, ensuring that our deployments are not only efficient but also cost-effective.

Migrating from Monolith to Microservices

Initially, we developed a monolithic backend system, chosen for its simplicity, ease of testing, and straightforward deployment. This approach was perfectly aligned with the early requirements of the project, providing a solid foundation in its initial development stages. As the client decided to expand their business model by offering their solution as a service to partners, we jointly decided to migrate from a monolithic to a microservices architecture. If you’re interested in learning more about the principles guiding the transition from monolithic architecture to microservices, we invite you to explore this blog post.

Next Steps Forward

Our expert team builds secure, high-performance backends for web and mobile apps using top cloud technologies (Azure, GCP, AWS). We leverage CI/CD best practices to automate everything, ensuring rapid updates and smooth teamwork.

Let’s discuss a Future-State Architectural Design that will help your business grow.

Contact us today to get started!

Let's arrange a consultation