Multi Tenant Architecture via Dependency Injection: Part 1
Chances are, if you’ve been developing web applications for a number of years, you’ve noticed a few ways of provisioning your application for your users.
- Your users all log into a single codebase. It might be load balanced, cached, the whole nine yards, but everybody is on the same build and accesses the same data.
- Your users log into multiple codebases and/or multiple databases. Perhaps you have some high-powered customers who want custom functionality, and so you forked your codebase and created them their own functionality. Most likely, you can update your customer’s builds independently of one another.
- Your users all log into a single codebase, but you use flags, connection strings, config files, etc., to logically separate out your users from one another. Your high-powered customers see their custom functionality, but it’s part of the main codebase and you are able to update your application in one fell swoop.
We’re going to talk more about option #3 — the Multi-Tenant Architecture. There is PLENTY of reading material out there for you to learn more about multi-tenancy. Suffice it to say that it allows you to satisfy multiple sets of requirements for your multiple customers via one codebase, and your customers, most likely, are logically separate in the database(i.e. they use one schema and one top-level data store) instead of physically separate.
In the past, (and present, for novice developers), it was a usual practice to utililze config files, database flags, etc., to turn on and off functionality. Your code may have looked something like:
<% if (showThisFunctionality) %> ; <% else %>; <% end if %>
Fig 1: Classic ASP
By utilizing a dependency injection framework you can provide features to your users, based on configuration and setup, without having to use logic switches inside of your application. This is above and beyond the existing benefits of using a dependency injection framework, which is not the scope of this essay but can be enumerated here, for instance. Our example will use Ninject and ASP.NET MVC, although you could use any DI and application frameworks.
Our challenge for this example will be to:
- dynamically set which service object(s) we are using to perform our application logic
- dynamically set which view we are returning
- dynamically set which partial views we are using
Our example will utilize dependency injection to provide order fulfillment functionality for our customers. For goal #1, we will use Ninject’s MVC integration to provide service objects for our controllers. Once Ninject is wired up, we can have our service objects perform our application logic against our Model, and as a matter of fact, Behavioral design patterns are a good fit here. Goal #2 is achievable fairly easily with ASP.Net MVC. Setting which view you’re returning via any MVC framework is often a very simple operation and comes down to returning a string variable which represents the view in question. For ASP.NET MVC we can ‘
returnView("viewName")' where “viewName” is the name of the view we wish to return. There is also the obvious
"return View(viewNameVariable)" override. Our example will use a configuration object to set which view is being called. Accomplishing goal #3 can be thought of as an extension of #2, but “finer grained”. Where you have general and reusable functionality within your view, your partial views allow you to have specific customizations exposed to your user in a consistent fashion.
In Part 2 we will investigate both integrating Ninject with ASP.Net MVC as well as what shape our framework will take. In Part 3 we will finish up our framework architecture and explore how to use it to provide features to our users.