Recently I’ve been working on adding multi-tenancy to a web application based on the excellent S#arp Architecture and thought I’d share what I have so far.
This implementation is mainly based on this post from Robert Johnson, and uses a separate database for each tenant, along with a master database to hold the details of the tenants (and anything else you wish). The main drawback with using separate databases is handling the migrations, I haven’t yet got a solution to this but will look at a way of automating it using something like Migrator.Net in a future post.
All of the domain entities inherit from SharpArch.Core.DomainModel.Entity, and I use a marker interface to indicate which entities are part of the tenant domain model.
We can now create our domain entities for the tenants:
And we create an entity, to be stored in the master database, to represent each tenant:
The application will make use of a separate NHibernate session for each tenant, identified by a key. For the master database the default session will be used. So, we create an interface that will provide access to the key:
And we implement this interface based on the method we choose to identify tenants, in this case based on the subdomain:
If a different method of identifying tenants is required, say by a query string parameter, then it is just a case of providing a different implementation of ITenantContext.
We can now create a multi-tenant repository that uses ITenantContext to select the NHibernate session based on the key:
Next we need to create a repository interface:
and implementation for our multi-tenant entities:
Our controllers can now make use of ICustomerRepository without having to worry about any of the multi-tenancy issues.
We also need to update the TransactionAttribute so that it makes use of the appropriate NHibernate session:
Next up, we need to update the initialisation in Global.asax.cs so that we create a session factory for the master database and also for each tenant:
In the code above, the standard NHibernate.config file is used to configure the master database. Whilst NHibernate.tenant.config, along with the connection string provided by the Tenant, is used to configure the tenant databases:
This code iterates through the list of tenants from the master database, setting the connection string and session factory key from the tenant properties and adds the configuration to NHibernate.
All that is left is to generate the mappings. In AutoPersistenceModelGenerator we move the creation of the standard mapping configuration into a method so that it can be overridden:
And derive from AutoPersistenceModelGenerator to create our multi-tenant mapping configuration:
Then we add a method to AutomappingConfiguration to determine if a type is a multi-tenant entity (by checking to see if the type implements our IMultiTenantEntity interface):
and update the ShouldMap method to only map entities that are not multi-tenant:
It is then a case of deriving from AutomappingConfiguration to map the multi-tenant entities: