The Mission:
Build a web application as though it’s for a single customer (tenant) and add multi-tenancy as a bolt-on feature by writing only new code. There are flavors of multi-tenancy, in this case I want each tenant to have its own database but I want all tenants to share the same web application and figure out who’s who by looking at the host header on the http request.
The Plan:
To pull this off, we’re going to have to rely on our SOLID design principles, especially Single Responsibility and Dependency Inversion. We’ll get some help from these frameworks:
Game on:
Let’s take a look at a controller that uses NHibernate to talk to the database. I’m not going to get into whether you should talk directly to NHibernate from the controller or go through a service layer or repository because it doesn’t affect how we’re going to add multi-tenancy. The important thing here is that the ISession
is injected into the controller, and we aren’t using the service locator pattern to request the ISession
from a singleton.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Alright, now it’s time to write some new code and make our web application connect to the correct database based on the host header in the http request. First, we’ll need a database to store a list of tenants along with the connection string for that tenant’s database. Here’s my entity:
1 2 3 4 5 6 7 8 |
|
I’ll use the repository pattern here so there is a crisp consumer of the ISession
that connects to the lookup database rather than one of the tenant shards. This will be important later when we go to configure Unity.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
So now we need a dedicated ISessionFactory
for the lookup database and make sure that our NHibernateTenantRepository
gets the right ISession
. It’s not too bad, we just need to name them in the container so we can refer to them explicitly.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
Hopefully that’s about what you were expecting since it’s not really the interesting part. The more interesting part is configuring the ISession
that gets injected into the UserController
to connect to a different database based on the host header in the http request. The Unity feature we’re going to leverage for this is the LifetimeManager
. This is an often overlooked feature of IoC containers.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
Here we’re using a custom PerHostLifetimeManager
. This tells Unity to maintain a session factory per host. When Unity runs across a host it doesn’t have a session factory for, it will run the InjectionFactory
block to create one using the connection string associated with that tenant.
Since multiple simultaneous requests will be trying to get and set values with the same key, we need to make sure our PerHostLifetimeManager
is thread safe. That’s pretty easy since Unity comes with a SynchronizedLifetimeManager
base class that takes care of the fact that Dictionary
isn’t thread safe.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
So what did we accomplish? Well we didn’t touch any of our existing application code. We just wrote new code and through configuration we added multi-tenancy! That’s pretty cool, but was it worth it? Well, the goal in itself isn’t super important, but this exercise can certainly highlight areas of your codebase where you might be violating the single responsibility principle or leaking too many infrastructure concepts into your application logic.