Implementing Multitenancy in LMS Solution
Overview
Multitenancy is an architectural pattern where a single instance of software serves multiple tenants. In our LMS (Learning Management System) solution, this means that each educational institution (tenant) will have isolated data and customized experiences while sharing the same application infrastructure. This document outlines the changes made in our application to implement multitenancy using the ABP Framework, including database modifications, code updates, tenant resolution mechanisms, and log review processes.
Database and schema Stratagy
Schema
We have decided to use a single schema for all tenants and the host (less complexity).
Database
Each tenant will have seperated database, but all tables which not implement the IMultitenant Interface will be stored in the shared database.
important abp tables which not multitenant :- AbpSettings AbpClaimTypes AbpBackgroundJobs AbpLanguages AbpFeatureValues OpenIddict module tables Saas module tables
Db Migration
run the db migrator will adding the new migrations and the data seeds for all tenant databases and the host database.
Tenant Resolving
we will use 4 tenant resolver :-
if the current user is authenticated, the current tenant value will be resolved from the current user.
settings tenant resolver :- which obtain the tenant id from the AbpSettings Multitenancy.TenantId value.
devnas domain resolver (production only):-
which determine the current tenant from the domain host value.
- debug tenant resolver (debugging only) :-
which get the tenant name or id from the appsettings.json file CurrentTenant:Name value.
- Example :-
{
"CurrentTenant": {
"Name": "altharaa"
}
}
Adding Tenants
Adding new tenant
[Instructions for adding new tenants will be provided here.]
Migrate an existing tenant (this section might be changed soon)
please follow the steps below to adding tenant with an existing databse :-
- prepare the tenant database.
- Login with the host account (/account/devnas/login).
- from the admin dashboard go to (saas/tenants).
- add new tenant by clicking Add New Tenant and submitting the form, please let the Use the Shared Database Default connection string option checked.
- go to the shared database and change the tenantID as per the tenant database
- migrate all settings and features from the tenant database to the shared database, using the following command.
INSERT INTO SharedDatabase.AbpSettings SELECT * FROM TenantDatabase.AbpSettings;
INSERT INTO SharedDatabase.AbpFeatureValues SELECT * FROM TenantDatabase.AbpFeatureValues;
- go to the host dashboard again, (saas/tenants).
- click on the actions beside the tenant which you added, then click (Database Connection Strings).
- submit the form by entering the Tenant database connection string.
Challanges
Background Jobs
Challange : background jobs does not support multitenancy in abp framework.
Solution :we have to adding the tenantID in the background job args, and implementing CurrentTenant.Change in the background job itself.
- Example :-
//background job args
public class CreateManySubscriptionCardArgs
{
public string Group { get; set; }
public int NumberOfCards { get; set; }
public Guid? TenantId { get; set; }
}
// background job execution
public class CreateManySubscriptionCardJob
: AsyncBackgroundJob<CreateManySubscriptionCardArgs>, ITransientDependency
{
private readonly ICurrentTenant CurrentTenant;
public override async Task ExecuteAsync(CreateManySubscriptionCardArgs args)
{
using (CurrentTenant.Change(args.TenantId))
{
await _subscriptionCardRepository
.CreateManyAsync(args.Group, args.ExpirationDate, args.NumberOfCards,
args.BundleId, args.BundleType, args.PointOfSaleId);
}
}
}
Logging
Challange : tracking the logs will be harder, because all tenants will have the same log files.
Solution : [Currently, there is no solution provided for this challenge.]
Deployment
Challange : deploy any fix to specific tenant will require to shutdown the whole application instance, which will affect the other tenants.
Solution : [Currently, there is no solution provided for this challenge.]
Breaking changes
Main language detector :- you have to set the LMS.Platform.MainLanguage settings value to the prefered language for the tenant, The Default language will be arabic.
AuthGeneratorClient :- you have to set the LMS.App.BaseUrl settings value to the tenant platform url Or the AuthGeneratorClient will not work, Note that the auth server url will be seeded to the devnas default auth server url https://saasauth.devnas-us.com/.