There was an error in this gadget

Wednesday, March 14, 2012

MultiTenant Mode for the DDD/CQRS Base Project

Of lately i have been working on how to enable the DDD/CQRS Base Project to work in a multi tenant environment. If you have not heard about multi tenancy here's a short description picked up from Wikipedia

Multitenancy refers to a principle in software architecture where a single instance of the software runs on a server, serving multiple client organizations (tenants). 

This article out here explains the 3 main approaches that can be taken to develop a multi tenant application.As with most things in software each approach has its merits and demerits :) , however here we are mainly interested in the Separate Databases and Shared Database, Separate Schemas approaches.

Multi Tenant Data Architecture

Now in both of these approaches each tenant would get a separate database/schema.

From a design stand point for this to work, we would need the following details.

1) If we have N tenants we would need to have N databases/schema having identical table structures.
2) The connection properties for each of these databases will have to be know to the application at start up   time.
3) The connection properties need to be kept at a know place, so that the application can read it at start up and create the required data-sources. A know place here could be but is not restricted to a properties file, hard coded in the application, a separate common schema which only holds data common to all tenants.

Yup you guessed it right we went ahead with " a separate common schema which only holds data common to all tenants".

Challenge that needed to be overcome -- Hibernate :)

We use hibernate as our ORM tool, Hibernate creates its Session Factory at start up, this session factory would refer to a data-source to establish a connection with database. Once the connection is established it generates the meta data required for it to map domain classes with the data base tables.

However now we would need Hibernate to refer to different data sources based on the tenant that logs into the application :) , Hmmm now it is getting interesting.

The way we managed to get this done is feed Hibernate with a TenantRoutingDataSource instead of a regular Data Source.

This TenantRoutingDataSource gets invoked by Hibernate each time it needs access to a Datasource in order to create a connection with the database, the only smart thing that this TenantRoutingDataSource does is to look up for the tenant Id and dish out the appropriate Data source :).

Bean diagram for multi tenant session factory.

Now the question arises "how the hell does this TenantRoutingDataSource ever get hold of the tenant Id" ?

and and

How does this fellow(TenantRoutingDataSource) know about all the data sources for each tenant !!??

Ok one at a time and as always we'll start with the easier first question :)

How the hell does this TenantRoutingDataSource ever get hold of the tenant Id ?

To start with the login screen needs to be modified slightly and will begin to look something similar to the below.

Example login screen for a multi tenant application.

The login screen will need to capture the tenant Id with which the user intends to login.

This tenant Id then gets set in a Thread Local class and the user session. Sample code to get this going can be seen here.

Now the TenantRoutingDataSource can refer to the Thread Local and get hold of the Tenant Id.

Now the slightly more tricky

How does this fellow know about all the data sources for each tenant !!??

Bean diagram for multi tenant session factory.

Now look at how the TenantRoutingDataSource depends on the datasourceFactory which in turn depends on the CommonSchema.

Rings a bell, Common Schema which holds all the details common to all tenants which includes the connection properties for there respective databases/schema ....

Minimal common schema may look like this

Minimal common schema

If you use a mysql database then the tenant_customisation_details table could look this


So the datasourceFactory looks into the common schema reads all the connection properties and creates a map of the tenant Id's and corresponding data sources at start up.

Once the map is built it is passed into the TenantRoutingDataSource , now our new friend TenantRoutingDataSource has both the map and the tenant Id, each time it is called by hibernate it looks,
first for the tenant Id using the Thread Local class and then with the tenant Id looks into the map for the tenant specific Datasource and passes this datasource out to Hibernate.

This allows Hibernate to carry on in its merry way :).

Ok now for the not so smart bit of this approach :), one cannot leverage on Hibernate's second level caching abilities, however in our particular use case it is not much of a draw back as we do not use Hibernate to query the system :) , Hibernate comes into play only on the command side, more on this can be found here.

Hibernate 4 has come up with a explicit API in order to support tenant aware applications, more details about it can be found here. This should take care of the second level cache too.

Do you think some thing is not quite right in the given approach,hit me with a comment.

No comments:

Post a Comment