Building Multi-tenant Web API using dot net core and best practices (Tutorial)
Business needs to grow in order to be successful and handle an increasing number of clients and partners, and if a company is not ready to respond to this load then there is a big chance that opportunities can be missed. This brings a topic of scalability into the game, as one of the main requirements that a company should address. As one of the possible ways to address this requirement is to build a multi-tenant solution. And as this topic gains more on importance, lots of options are available to achieve this, for example using Microsoft Elastic database (elastic tools). However in particular cases, like the case I faced on my project, not all of the product requirements could be satisfied with the already available options. This brought me to the idea of gathering my experience on this topic and presenting it below.
As we all are aware there are two main approached to tackle applications scaling — horizontal and vertical. Horizontal scaling will bring you the benefit of scaling on the fly and will imply dealing with multiple databases as each tenant has its own database/shard. Vertical approach to scaling presumes having one database that serves several tenants.
In my article I will address the approach of horizontal scaling with a step-by-step guide on how to build a multi-tenant web API application.
If you would like to refresh some aspects of multi-tenant architecture or what are pros and cons it
brings to the project, then I recommend visiting these resources:
- Why Cloud Architecture Matters: The Multi-Instance Advantage over Multi-Tenant
- Why Multi-Tenant Application Architecture Matters in 2017
- Design patterns for multi-tenant SaaS applications and Azure SQL Database
Let’s briefly take a look at the architecture first. The example below is designed based on N-tire
architecture and has the following layers:
- Presentation layer or web api
- Service layer that will accommodate all the business logic
- Data access layer that is implemented using UnitOfWork and Repository patterns. As ORM in this example I used Entity Framework Core.
The key component of tenant separation is ContextFactory that contains logic to get the tenant id from HTTP header, retrieve a tenant database name using DataBaseManager and replace a database name in the connection string. As a result a database context (Entity Framework context) is created.
The diagram below demonstrate this architecture.
As you can see architecture is not that complicated here, and skimming through it, I’d suggest to
focus on the steps to implement it.
1. Create ContextFactory
As I mentioned before ContextFactory is key component of whole architecture. It construct Entity Framework context (in current example DeviceApiContext) with specific to tenant database
Source code of ContextFactory available Here
3. Add the data base manager
Database manager orchestrates all tenants metadata such as tenant database name, activation
status of tenants (activated/deactivated) and bunch of other properties. To demonstrate a base principle I used dictionary in the current solution. Later on dictionary should be replaced with more appropriate solutions, like SQL or NoSQL database that contains tenants metadata. This idea is similar to shard map manager that is used in Microsoft Elastic Tools. Also tenant metadata may includes fields to store database name, options to activate/deactivate tenants, even tenant styles for front-end application based on CSS/SASS/LESS file an on and on
Source code of DataBaseManager available Here
4. Add Unit of Work class (contains commit to specific context method)
UnitOfWork solves two tasks. It commits all changes made by Entity Framework in entities and dispose specific context.
Source code of UnitOfWork available Here
5. Add Generic Repository class.
Repository will make changes in EF entities and Unit of Work will commit changes to tenants database. Be aware that EF making changes in memory, using Tracking Mechanism.
Source code of Repository available Here
5. Add tenant header operation filter.
TenantHeaderOperationFilter class will add tenant id field to all API calls (as HTTP header). In solutions which uses OIDS services e.g IdentityServer or auth0.com tenant can be injected to JWT token.
This is how API will look like after filter applied.
Current example of service class (DeviceService.cs) contains functions to retrieving device by id and add new device for specific tenants. Source Code of service layer available Here
In this article I was explained how to build “ready to go” Multi tenant solution and given some suggestions how it can be used in your product/business. As I mentioned before this solution is ready to go so it can be used as “Boilerplate” project or partially.
The project’s source code available in Git repository here
Original article here.