The diagram below depicts the architecture for Lab 2 that you deployed using the ./deployment.sh script.
We provide an example experience of an anyonymous user registering for their own tenant. The sample app now includes a login UI, which also displays sign-up option. Clicking on it allows you to provide data about your new tenant and submit that information to the system’s registration service. This service will then create and configure all the resources needed to introduce a new tenant into the system.
In addition to self-registration, the SaaS provider can also add and manage tenants. (In a multi-tenant SaaS environment, the SaaS provider would need a way to manage tenants: Create new ones, enable/disable them, add/update metadata, etc.). The sample admin app represents this experience and we’ll be taking a closer look at it later.
The shared services refer to the common set of services that are core to any SaaS environment. These services provide all the mechanisms that are needed to have a universal view of how tenants are onboarded, managed, and operated in SaaS environment.
The Tenant Registration service allows new tenants to register themselves and onboard to your SaaS application. There are a few moving parts to this registration experience. This Tenant Registration service is responsible for orchestrating interactions with user management and tenant management that are part of the onboarding experience.
The User Management service allows us to add, update, disable and get users. The users are stored in Amazon Cognito, which is otherwise known as the Identity Provider in the architecture.
The Tenant Management service centralizes all of the configuration and operations that can be performed on a tenant. This includes get, create, update, activate, and disable tenant functionality. Tenant details are stored in our Fauna database.
The diagram below depicts the tenant onboarding process and how Registration service leverages other services to orchestrate the flow.
The sign-up process for a tenant is a combination of a few steps.
When we create a tenant, we add a new document to the tenant collection. We also create a tenantUser document to store the association between the tenant record and the tenant admin user created in Cognito. And finally, a child database is created, and it is hydrated with the Order Microservice and Product Microservice’s schema.
Our schema design to achieve multi-tenancy leverages a unique Fauna feature that allows the association of databases as children of another. The Fauna API key we manage belongs to the “parent” database, but it is allowed scoped access to each of it’s “child” databases. With this design, all our functions and queries are written as if there is only a single tenant. Up in the Lambda layer, a utility instantiates the Fauna client and scope it to the proper database (parent or specific child) depending on the context of the request. We dive into the details of this later in Lab 3.
One thing you will notice when you inspect the code, is that the tenant registration service (POST /registration) doesn’t require any authentication. This is by design since new tenants are unable to be authenticated. However, the endpoints that are used to create a tenant admin, create a tenant, and provision a user are protected. These endpoints can only be invoked from the Tenant Registration service.
To keep these internal endpoints private and protected, we have used the API Gateway Resource Policies feature. These policies ensure that the private REST entry points of our services are not publicly accessible.
Access to Fauna is authorized with an API Key, which is protected using KMS.
Whenever a Lambda function needs to access Fauna, it retrieves the Fauna API Key from the Parameter Store. The API Key is stored encrypted, using an encryption key managed by KMS.