Multitenant PRO

# Introduction

This feature helps in situations when it's required to setup multiple tenants in a single project. A common scenario is a SaaS product in which, instead of having a shared database for all the clients, each has its own and independent database(in the same or different server), but sharing the same source code and deployment. In this case, you would want to apply the same Mongock migration to each client's database.

multitenant overview

# How it works

The application is configured to work with N datasources and Mongock runs the migration for all of them(sequentially). To achieve this, the user has to configure a Mongock driver per datasource, pass them to the Mongock builder and execute the Mongock runner which will apply the migration for all the tenants.

# With Mongock Standalone

This is the simplest scenario as there is no ORM framework to deal with. It just requires passing a driver per tenant to the Mongock builder. This mechanism is standard and doesn't change among the different Mongock drivers/databases

MongockStandalone.builder()
.setDriverMultiTenant(
MongoSync4Driver.withDefaultLock(mongoClientTenant1, "db-tenant-1"),
MongoSync4Driver.withDefaultLock(mongoClientTenant2, "db-tenant-2")
)
//...

# With Springdata

While the core idea and how it works still remains, when using Springdata in a multitenant environment(independently of Mongock) it requires some extra work, which it's explained bellow.

# The challenge

Springdata hides all the complexity to connect to the database, which helps a lot, but it also means the developer needs to give some flexibility up . In this case, it's not straightforward specifying the database which we want to connect to.

Lets imagine the following scneario...


@RestController
public class ClientController {
private final ClientRepository clientRepository;
public ClientController(ClientRepository clientRepository) {
this.clientRepository = clientRepository;
}
@GetMapping("/api/{tenant}/clients")
public List<Client> getClients(@PathVariable("tenant") String tenant) {
return clientRepository.findAll();
}
}

The above code wouldn't work because of:

# The solution

When interacting with a Springdata repository in a multitenant environment, a mechanism to select the tenant's database you want to point to is required.


This normally gets the shape of a Tenant manager or selector , which is used by the internal Springdata classes, but also shared with any component that should be able to tell Springdata which tenant's database it wants to talk to.

Following the previous example, the solution would look like this:

@RestController
public class ClientController {
private final TenantManager tenantManager;
private final ClientRepository clientRepository;
public ClientController(TenantManager tenantManager, ClientRepository clientRepository) {
this.tenantManager = tenantManager;
this.clientRepository = clientRepository;
}
@GetMapping("/api/{tenant}/clients")
public List<Client> getClients(@PathVariable("tenant") String tenant) {
tenantManager.select(tenant);
return clientRepository.findAll();
}
}

Springdata would use the tenantManager to know which database point to. The implemenation depends on the database. For example for SQL, it requires to extend the class AbstractRoutingDataSource, while for MongoDB(example here), SimpleMongoClientDatabaseFactory.


The only bit missing is how to link all this to Mongock(it needs the tenantManager to select the tenant when applying the change units). For this, Mongock provides the interface TenantManager, whose implementation needs to be injected as a bean. There are two options:

# Resources