# Multitenancy

# Introduction

Multi-tenancy is a software architecture where a single instance of software runs on a server and serves multiple tenants. A good example would be GitHub where each user or organization has their separate work area. This concept is used while developing software that runs for different organizations.

Multi-tenancy is a key concept in building and delivering software-as-a-service (SaaS)solutions. The following image shows the two architecture for separating data.

Multitenancy in ExpressWebJs

⚠️ Multitenancy is only available in ExpressWebjs Objection and Mongoose ORM at the moment.

# Methods of multi-tenancy

We will like to share two basic models that are commonly used when partitioning tenant data in a SaaS environment.

  • Logical Separation of Data: In this approach, there is only one database for all tenants. Their data is separated by using some unique identifier for each client. The codebase is responsible for storing and retrieving data using this unique identifier.

  • Physical Separation of Data: This approach separates the data by provisioning different database for different tenants/clients. This helps us to scale our application as the number of clients grows and also scale the database as per the clients need. In this guide, we’ll be talking about multitenant architecture with physical separation of data.

# Configuration

Let’s dive into the implementation of the multi-tenant system. First we need to adjust our .env file by adding the following:

DB_MULTITENANCE = true;
DB_TENANT = null; //Current tenant for migration and seed
Copied!

Next we will add our multitenancy configuration settings in Config/Database.ts file.

  /*
  |--------------------------------------------------------------------------
  | Database Multitenance
  |--------------------------------------------------------------------------
  | Database multitenance can be activated by switching the value to true and can
  | be deactivated by switching it to false.
  |
  */
  database_multitenance: env("DB_MULTITENANCE", false),
 /*
  |--------------------------------------------------------------------------
  | Multitenance Connections
  |--------------------------------------------------------------------------
  |
  | Database multitenace connection enables interaction with multiple
  | SQL databases where each database is a tenant in your system.
  | The tenant array accepts an object of database connections (tenants).
  |
  */
    /*
  |--------------------------------------------------------------------------
  | Multitenance Connections
  |--------------------------------------------------------------------------
  | Database multitenance connection enables interaction with multiple
  | SQL databases where each database is a tenant in your system.
  | The tenant array accepts an object of database connections (tenants).
  |
  */
  multitenant_tenants: DBConnection.multitenant<TypeORMConfigurationType>("TypeORM", [
    {
      tenantName: "tenant_1",
      type: env("DB_DRIVER"),
      host: env("DB_HOST"),
      port: Number(env("DB_PORT")),
      username: env("DB_USER"),
      password: env("DB_PASSWORD"),
      database: env("DB_DATABASE"),
      entities: ["App/Model/*.ts"],
      logging: false,
      synchronize: false,
    },
    {
      tenantName: "tenant_2",
      type: env("DB_DRIVER"),
      host: env("DB_HOST"),
      port: Number(env("DB_PORT")),
      username: env("DB_USER"),
      password: env("DB_PASSWORD"),
      database: env("DB_DATABASE"),
      entities: ["App/Model/*.ts"],
      logging: false,
      synchronize: false,
    }
  ]),
Copied!

The objects in the multitenant_tenants section are the different databases(tenants) we are adding to our system.

⚠️ For Objection js, the following is needed for tenant migration.

# Tenant1

For tenant1, we need to set DB_TENANT = tenant_1; in .env file and restart our app.

This will enable our SchemaSetup file to switch to tenant1 migration ready state which will enable us generate migrations for tenant1.

# Tenant2

For tenant2, we need to set DB_TENANT = tenant_2; in .env file and restart our app.

This will enable our SchemaSetup file to switch to tenant2 migration ready state which will enable us generate migrations for tenant2.

# Querying data from database

Tenant name can be passed down to the repository as second parameter, which communicates with the datasource.

import { UserModel } from "App/Model/UserModel";
import { IUserModel } from "App/Model/IUserModel";
import { TypORMRepository } from "Elucidate/Repository/TypeORM";

export class UserRepository extends TypORMRepository<IUserModel> {
  constructor() {
    super(UserModel, "tenant_1");
  }
}
Copied!