What Is Multi Tenancy?

Wikipedia: "Software Multitenancy refers to a software architecture in which a single instance of a software runs on a server and serves multiple tenants. A tenant is a group of users who share a common access with specific privileges to the software instance. With a multitenant architecture, a software application is designed to provide every tenant a dedicated share of the instance including its data, configuration, user management, tenant individual functionality and non-functional properties. Multitenancy contrasts with multi-instance architectures, where separate software instances operate on behalf of different tenants"

Multi-tenancy is used to create SaaS (Software as-a Service) applications (cloud computing). There are some types of multi-tenancy:

Multiple Deployment - Multiple Database

This is not multi tenancy actually. But, if we run one instance of the application for each customer (tenant) with a seperated database, we can serve to multiple tenants in a single server. We just make sure that multiple instance of the application don't conflict with each other in same server environment.

This can be possible also for an existing application which is not designed multitenant. It's easier to create such an application since the application is not aware of multitenancy. But there are setup, utilization and maintenance problems in this approach.

Single Deployment - Multiple Database

ln this approach, we may run a single instance of the application in a server. For each user login, we check tenant of the user from a master database and get database informations (connection string) of the tenant. Then we can store connection string into a session-like variable and perform all database operations using this tenant-specific connection string.

In this approach, application should be designed as multi-tenant in some level. But most of the application can remain independed from multi-tenancy. This approach is also some setup, utilization and maintenance problems. We should create and maintain a seperated database for each tenant.

Single Deployment - Single Database

This is the most real multi-tenancy architecture: We only deploy single instance of the application with a single database into a single server. We have a TenantId (or similar) field in each table (for a RDBMS) which is used to isolate a tenant's data from others.

This is easy to setup and maintain. But harder to create such an application. Because, we must prevent a Tenant to read or write other tenant datas. We may add TenantId filter for each database read (select) operation. Also, we may check in every write, if this entity is related to the current tenant. This is tedious and error-prone. But ASP.NET Boilerplate helps us here by using automatic data filtering.

This approach may have performance problems if we have many tenants with huge datas. We may use table partitioning feature of relational databases and/or group tenants in different servers.

Multi-Tenancy in ASP.NET Boilerplate

ASP.NET Boilerplate provides infrastructure to create a single-deployment, single-database, multi-tenant architecture.

Enabling Multi Tenancy

Multi-tenancy is disabled by default. We can enable it in PreInitialize of our module as shown below:

Configuration.MultiTenancy.IsEnabled = true; 

Host vs Tenant

First, we should define two terms used in a multi-tenant system:

Session

ASP.NET Boilerplate defines IAbpSession interface to obtain current user and tenant ids. This interface is used in multi-tenancy to get current tenant's id. Thus, it can filter datas based on current tenant's id. We can say these rules:

See session documentation for more information on the session.

Data Filters

While retrieving entities from database, we must add a TenantId filter to get only current tenant's entities. ASP.NET Boilerplate automatically does it when you implement one of two interfaces for your entity: IMustHaveTenant and IMayHaveTenant.

IMustHaveTenant Interface

This interface is used to distinguish entities of different tenants by defining TenantId property. An example entitiy that implements IMustHaveTenant:

public class Product : Entity, IMustHaveTenant
{
    public int TenantId { get; set; }
        
    public string Name { get; set; }
    
    //...other properties
}

Thus, ASP.NET Boilerplate knows that this is a tenant-specific entity and automatically isolates entities of a tenant from other tenants.

IMayHaveTenant interface

We may need to share an entity type between host and tenants. So, an entity may be owned by a tenant or the host. IMayHaveTenant interface also defines TenantId (similar to IMustHaveTenant), but nullable in this case. An example entitiy that implements IMayHaveTenant:

public class Role : Entity, IMayHaveTenant
{
    public int? TenantId { get; set; }
        
    public string RoleName { get; set; }
    
    //...other properties
}

We may use same Role class to store Host roles and Tenant roles. In this case, TenantId property says if this is an host entity or tenant entitiy. A null value means this is a host entity, a non-null value means this entity owned by a tenant which's Id is the TenantId.

IMayHaveTenant is not common as IMustHaveTenant. For example, a Product class can not be IMayHaveTenant since a Product is related to actual application functionality, not related to managing tenants. So, use IMayHaveTenant interface carefully since it's harder to maintain a code shared by host and tenants.

Saving Entities

A tenant user should not create/edit other tenant entities. ASP.NET Boilerplate checks it on saving changes to database if related data filters are enabled. 

See data filters document for more information on data filters.