Trung Pham Duy - March 2024
Checkout the presentation about
Saas
A common approach to build SaaS solutions
Tenants can refers to:
Tenantis a catch-all term, and teams define what it means to them!
| Single-tenant architecture | Multi-tenant architecture |
|---|---|
| Each tenant has a dedicated instance of the application and its resources | Multiple tenants share a single instance of the application and its resources |
| Scaling requires provisioning and managing individual instances for each tenant | Easier scalability as multiple tenants can be accommodated within a single instance |
| More secure due to isolated environments | Security measures involve ensuring data isolation and preventing unauthorized access |
| Less cost-efficient: hardware and software for each user is paid separately | More cost-efficient: thanks to shared infrastructure and platform |
| Independent maintenance and upgrades for each tenant’s instance | Centralized maintenance, updates, and upgrades that apply to all tenants |
| Slower deployment: each tenant requires individual setup and configuration | Faster deployment: new tenants can be added within the existing setup |
With multiple customers using the same system resources and computing power, companies might start suffering from “noisy neighbor” syndrome, where they can’t access the resources they need, and operations slow down.
By outsourcing data and operations to an external cloud managed by a third-party service provider, companies risk losing access to critical data and information in the case of a technical error. Also, cloud environments are susceptible to downtime; although, it’s minimal with the top providers.
Data can be physically or virtually isolated.
|
|
❌ Risk of exposing one tenant’s data to another tenant or updating the wrong tenant’s data (e.g., if a developer misses a WHERE clause to filter on the tenant id)
❌ No tenant isolation
✔️ One database schema to maintain and a simple schema update rollout process—it only needs to be applied once
✔️ Manage the High Availability/Disaster Recovery/maintenance operation/monitoring strategy for just one database
✔️ Limited development/application code complexity—single schema, single database to connect to
✔️ Adding new tenants is easy—no processes needed around database/schema provisioning or connection determination
❌ Any query or data modification must includes a predicate to restrict the operation to a specific tenant id
❌ Must remember to update the RLS policy as new tables are added over time
❌ Can’t easily restore a single tenant’s data
❌ Limited to scaling-up hardware, rather than scaling out
❌ Risk of “noisy neighbors”—tenants can impact the performance of the system for all others due to a lack of isolation and all competing for the same resources
❌ One-size-fits-all performance tuning and stability—tenants’ data volumes and usage can vary dramatically, impacting things such as execution plans making it more difficult to optimize performance across every tenant
❌ As the number of tenants and data per tenant grows, maintenance activities take longer, potentially impacting all tenants.

✔️ Tenant data has some more isolation (but still within the same database)
✔️ No RLS needed; reduced risk of missing a WHERE clause to limit to specific tenant’s data
❌ Still a risk of querying the incorrect schema (e.g., specifying the schema for an object when it should have instead come from the user account’s default schema—usual best practice is include schema prefixes, which can feel unnatural)
❌ Still limited data isolation
✔️ 1 database to manage High Availability/Disaster Recovery/maintenance operation/monitoring strategy for
✔️ Extra scope and control over some tenant-specific maintenance activities
❌ Schema updates more involved, needing to be rolled out to n tenants
❌ Added query complexity: use proper schema
❌ Can’t easily restore a single tenant’s data (although it’s a slightly better process than approach 1 due to isolation of tenant data)
❌ Adding new tenants is more involved as new schemas/user accounts need to be created
❌ As the number of tenants grows, there will be a lot of database objects being created to manage and maintain
✔️ Data is partitioned into smaller tables, with smaller indexes
✔️ Optimizations could be made at an individual tenant’s schema level
❌ Limited to scaling-up (vertical scaling), rather than scaling out (horizontal scaling)
❌ Risk of “noisy neighbors”—tenants can impact the performance of the system for all others due to limited level of isolation and all competing for the same resources
Each tenant has their own database

✔️ Highest level of tenant isolation, supporting options for shared server and/or isolated servers
❌ Potentially more servers to patch and keep secure
✔️ Maintenance jobs can be managed and customized per tenant
✔️ Can easily restore/relocate/clear down a tenant’s data
✔️ No added query complexity
❌ Adding new tenants is more involved, as new schemas need to be created
❌ As the number of tenants grows, there will be more databases being created to manage and maintain
❌ Some added complexity to maintain a registry of tenant-db mappings/application code to determine which connection to use
✔️ Scale-out and scale-up are both options—tenants can be spread over multiple servers
✔️ Choose to balance between cost (higher tenant density/fewer servers) and performance (lower tenant density/more servers)
✔️ Control over “noisy neighbor” risks

✔️ Some tenant isolation possible in general over approach #1
❌ Tenants still share a database and schema with others (same RLS mitigation applies as approach #1)
✔️ Choose to balance between overhead of more databases to maintain (lower tenant density) versus fewer (higher tenant density)
✔️ Possible to relocate a tenant’s data (although harder than approach #3)
❌ More maintenance overhead than approach #1
✔️ Scale-out and scale-up are both options—tenants can be spread over multiple servers
✔️ Choose to balance between cost (higher tenant density/fewer servers) and performance (lower tenant density/more servers)
Parties |
|
✔️ Work seamlessly as much as possible!
tenantId to all your controllers, application services, repositories or domain services…✔️ When a customer/tenant wants to setup your solution to his own servers, you should be doing that without any code changes.
tenantId for New EntitiesHereafter, sample codes are in .NET Core
Application code & services should be stateless!
Where should we save the state?🤔
How to determine the current tenant?🤔
CurrentUserTenantResolveContributorQueryStringTenantResolveContributorRouteTenantResolveContributorHeaderTenantResolveContributorCookieTenantResolveContributorDomainTenantResolver







tenantId for New Entities

😊 Easy to implement. All tenants are in the same version
😡 May get too long time for big number of tenants and data
😡 All tenants wait for all upgrade progress
😊 Upgrading is distributed to time. A tenant does not wait for another
😡 First user may wait too much and see timeout exception.
😡 Hard to implement (concurrency problems)!
Ideal as upgraded tenants use the new application, other tenants use the old application.
😊 Minimum wait time for a tenant
😊 Upgrading can be scheduled for tenants
😊 Run A/B tests and see bugs before anyone else
😡 Requires multiple app servers
😡 Hard to maintain and monitor