Showing posts with label Multi-tenancy. Show all posts
Showing posts with label Multi-tenancy. Show all posts

Friday, November 28, 2008

Multi-tenancy part 2: Components and Context

In my previous post, Multi-tenancy part 1: Strategy, I talked about some of the high level decisions we have to make when building a single software product for multiple users with diverse requirements. Today I'm going to look at implementing basic multi-tenancy with Suteki Shop. I'm going to assume that my customers have identical functional requirements, but will obviously need to have separate styles and databases. Some other simple configurable items will also be different, such as the name of their company and their contact email address.

But first I want to suggest something that's become quite clear as I've started to think more about implementing multi-tenancy. I call it:

Hadlow's first law of multi-tenancy: A multi-tenanted application should not look like a multi-tenanted application.

What do I mean by this? The idea is that you should not have to change your existing single-tenant application in any way in order to have it serve multiple clients. If you build your application using SOLID principles, if you architect it as a collection of components using Dependency Injection and an IoC container, then you should be able to compose the components at runtime based on some kind of user context without changing the components themselves.

I am going to get Suteki Shop to serve two different tenants without changing a single existing line of of (component) code.

We are going to be serving two clients. The first one is our existing client, the mod-tastic Jump the Gun. I've invented the new client zanywear.com. I just made the name up, it's actually a registered domain name but it's not being used. We're going to serve our clients from the same application instance, so we create a new web site and point it to an instance of Suteki Shop. Now we configure two host headers (AKA Site Bindings) for the web application:

test.jumpthegun.co.uk
zanywear.com

multitenanted-iis

For testing purposes (and because I don't own zanywear.com :) I've added the two domains to my C:\WINDOWS\system32\drivers\etc\hosts file so that it looks like this:

# Copyright (c) 1993-1999 Microsoft Corp.
#
# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
#
# This file contains the mappings of IP addresses to host names. Each
# entry should be kept on an individual line. The IP address should
# be placed in the first column followed by the corresponding host name.
# The IP address and the host name should be separated by at least one
# space.
#
# Additionally, comments (such as these) may be inserted on individual
# lines or following the machine name denoted by a '#' symbol.
#
# For example:
#
#      102.54.94.97     rhino.acme.com          # source server
#       38.25.63.10     x.acme.com              # x client host
127.0.0.1     localhost
127.0.0.1     test.jumpthegun.co.uk
127.0.0.1     zanywear.com

Now when I browse to test.jumpthegun.co.uk or zanywear.com I see the Jump the Gun website.

The task now is to choose a different database, style-sheet and some basic configuration settings when the HTTP request's host name is zanywear.com. Conventiently Suteki Shop has two services that define these items. The first is IConnectionStringProvider which provides (you guessed it) the database connection string:

namespace Suteki.Common.Repositories
{
    public interface IConnectionStringProvider
    {
        string ConnectionString { get; }
    }
}

And the other is the somewhat badly named IBaseControllerService that supplies some repositories and config values to be consumed by the master view:

using Suteki.Common.Repositories;
namespace Suteki.Shop.Services
{
    public interface IBaseControllerService
    {
        IRepository<Category> CategoryRepository { get; }
        IRepository<Content> ContentRepository { get; }
        string GoogleTrackingCode { get; set; }
        string ShopName { get; set; }
        string EmailAddress { get; set; }
        string SiteUrl { get; }
        string MetaDescription { get; set; }
        string Copyright { get; set; }
        string PhoneNumber { get; set; }
        string SiteCss { get; set; }
    }
}

Note that this allows us to to set the name of the style-sheet and some basic information about the shop.

In order to choose which component is used to satisfy a service at runtime we use an IHandlerSelector. This interface was recently introduced to the Windsor IoC container by Oren Eini (AKA Ayende Rahien) specifically to satisfy the requirements of multi-tenanted applications. You need to compile the trunk if you want to use it. It's not in the release candidate. It looks like this:

using System;
namespace Castle.MicroKernel
{
    /// <summary>
    /// Implementors of this interface allow to extend the way the container perform
    /// component resolution based on some application specific business logic.
    /// </summary>
    /// <remarks>
    /// This is the sibling interface to <seealso cref="ISubDependencyResolver"/>.
    /// This is dealing strictly with root components, while the <seealso cref="ISubDependencyResolver"/> is dealing with
    /// dependent components.
    /// </remarks>
    public interface IHandlerSelector
    {
        /// <summary>
        /// Whatever the selector has an opinion about resolving a component with the 
        /// specified service and key.
        /// </summary>
        /// <param name="key">The service key - can be null</param>
        /// <param name="service">The service interface that we want to resolve</param>
        bool HasOpinionAbout(string key, Type service);
        /// <summary>
        /// Select the appropriate handler from the list of defined handlers.
        /// The returned handler should be a member from the <paramref name="handlers"/> array.
        /// </summary>
        /// <param name="key">The service key - can be null</param>
        /// <param name="service">The service interface that we want to resolve</param>
        /// <param name="handlers">The defined handlers</param>
        /// <returns>The selected handler, or null</returns>
        IHandler SelectHandler(string key, Type service, IHandler[] handlers);
    }
}

The comments are self explanatory. I've implemented the interface as a HostBasedComponentSelector that can choose components based on the HTTP request's SERVER_NAME value:

using System;
using System.Linq;
using System.Web;
using Castle.MicroKernel;
using Suteki.Common.Extensions;
namespace Suteki.Common.Windsor
{
    public class HostBasedComponentSelector : IHandlerSelector
    {
        private readonly Type[] selectableTypes;
        public HostBasedComponentSelector(params Type[] selectableTypes)
        {
            this.selectableTypes = selectableTypes;
        }
        public bool HasOpinionAbout(string key, Type service)
        {
            foreach (var type in selectableTypes)
            {
                if(service == type) return true;
            }
            return false;
        }
        public IHandler SelectHandler(string key, Type service, IHandler[] handlers)
        {
            var id = string.Format("{0}:{1}", service.Name, GetHostname());
            var selectedHandler = handlers.Where(h => h.ComponentModel.Name == id).FirstOrDefault() ??
                                  GetDefaultHandler(service, handlers);
            return selectedHandler;
        }
        private IHandler GetDefaultHandler(Type service, IHandler[] handlers)
        {
            if (handlers.Length == 0)
            {
                throw new ApplicationException("No components registered for service {0}".With(service.Name));
            }
            return handlers[0];
        }
        protected string GetHostname()
        {
            return HttpContext.Current.Request.ServerVariables["SERVER_NAME"];
        }
    }
}

It works like this: It expects an array of types to be supplied as constructor arguments. These are the service types that we want to choose based on the host name. The HasOpinionAbout method simply checks the supplied serivce type against the array of types and returns true if there are any matches. If we have an opinion about a service type the container will ask the IHandlerSelector to supply a handler by calling the SelectHandler method. We create an id by concatenating the service name with the host name and then return the component that's configured with that id. So the configuration for Jump the Gun's IConnectionStringProvider will look like this:

<component
  id="IConnectionStringProvider:test.jumpthegun.co.uk"
  service="Suteki.Common.Repositories.IConnectionStringProvider, Suteki.Common"
  type="Suteki.Common.Repositories.ConnectionStringProvider, Suteki.Common"
  lifestyle="transient">
  <parameters>
    <ConnectionString>Data Source=.\SQLEXPRESS;Initial Catalog=JumpTheGun;Integrated Security=True</ConnectionString>
  </parameters>
</component>

Note the id is <name of service>:<host name>.

The configuration for Zanywear looks like this:

<component
  id="IConnectionStringProvider:zanywear.com"
  service="Suteki.Common.Repositories.IConnectionStringProvider, Suteki.Common"
  type="Suteki.Common.Repositories.ConnectionStringProvider, Suteki.Common"
  lifestyle="transient">
  <parameters>
    <ConnectionString>Data Source=.\SQLEXPRESS;Initial Catalog=Zanywear;Integrated Security=True</ConnectionString>
  </parameters>
</component>

Note that you can have multiple configurations for the same service/component in Windsor so long as ids are different.

When the host name is test.jumpthegun.co.uk the HostBasedComponentSelector will create a new instance of ConnectionStringProvider with a connection string that points to the JumpTheGun database. When the host name is zanywear.com it will create a new instance of ConnectionStringProvider with a connection string that points to the Zanywear database. We configure our IBaseControllerService in a similar way.

The only thing left to do is register our IHandlerSelector with the container. When I said I didn't have to change a single line of code I was telling a fib, we do have to change the windsor initialization to include this:

protected virtual void InitializeWindsor()
{
    if (container == null)
    {
        // create a new Windsor Container
        container = new WindsorContainer(new XmlInterpreter("Configuration\\Windsor.config"));
        // register handler selectors
        RegisterHandlerSelectors(container);
        // register WCF integration
        RegisterWCFIntegration(container);
        // automatically register controllers
        container.Register(AllTypes
            .Of<Controller>()
            .FromAssembly(Assembly.GetExecutingAssembly())
            .Configure(c => c.LifeStyle.Transient.Named(c.Implementation.Name.ToLower())));
        // set the controller factory to the Windsor controller factory (in MVC Contrib)
        System.Web.Mvc.ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory(container));
    }
}
/// <summary>
/// Get any configured IHandlerSelectors and register them.
/// </summary>
/// <param name="windsorContainer"></param>
protected virtual void RegisterHandlerSelectors(IWindsorContainer windsorContainer)
{
    var handlerSelectors = windsorContainer.ResolveAll<IHandlerSelector>();
    foreach (var handlerSelector in handlerSelectors)
    {
        windsorContainer.Kernel.AddHandlerSelector(handlerSelector);
    }
}

The handler selector setup occurs in the RegisterHandlerSelectors method. We simply ask the container to resolve any configured IHandlerSelectors and add them in. The configuration for our HostBasedComponentSelector looks like this:

<component
  id="urlbased.handlerselector"
  service="Castle.MicroKernel.IHandlerSelector, Castle.MicroKernel"
  type="Suteki.Common.Windsor.HostBasedComponentSelector, Suteki.Common"
  lifestyle="transient">
  <paramters>
    <selectableTypes>
      <array>
        <item>Suteki.Shop.Services.IBaseControllerService, Suteki.Shop</item>
        <item>Suteki.Common.Repositories.IConnectionStringProvider, Suteki.Common</item>
      </array>
    </selectableTypes>
  </paramters>
</component>

Note that we are configuring the list of services that we want to be selected by the HostBasedHandlerSelector by using the array parameter configuration syntax.

And that's it. We now have a single instance of Suteki Shop serving two different clients: Jump the Gun and Zanywear.

multitenanted-websites

Today I've demonstrated the simplest case of multi-tenanting. It hardly qualifies as such because our two tenants both have identical requirements. The core message here is that we didn't need to change a single line of code in any of our existing components. You can still install Suteki Shop and run it as a single-tenant application by default.

In the next installment I want to show how we can provide different implementations of components using this approach. Later I'll deal with the more complex problem of variable domain models. Watch this space!

Wednesday, November 19, 2008

Multi-tenancy part 1: Strategy.

I want my eCommerce application Suteki Shop to be able to flexibly meet the requirements of any customer (within reason). How can we have a single product but enable our customers to have diverse implementations. I think the solution depends on the level of diversity and the number of customers. The right solution for a product with thousands of customers with little customisation is different from a solution for a handful of customers with very diverse requirements.

There are several axis of change to consider:

  1. Codebase. Do I have one codebase, or do I maintain a separate codebase for each customer?
  2. Application instance. Do I have one application instance to service all my customers, or does each customer have a separate one?
  3. Database schemas. Do I have one database schema, or do I have a different schema for each customer?
  4. Database instances. Do I have one database instance or separate ones for each customer?

Lots of customers, little customisation

Lets consider the two extremes. First, say I've got a product that I expect to sell to thousands of customers. My business model is premised on selling a cheap service to very many people. It's worth my while not to allow too much customisation because the effort to supply that simply wouldn't be worth it. If someone wants something significantly different from my thousands of other customers, I'll just tell them to look elsewhere. In this case I'd have a single code base, application instance, database schema and database. Effectively a single application will service all my customers.

In this case the main technical challenge will making sure that each user's data is keyed properly so they perceive it as a single application servicing their needs only. It would be a major failure if one customer could see another's data. Think of an on-line email service such as hotmail. Sometimes we want to allow customers to see each other's data, think Facebook, but that interaction needs to be tightly controlled.

Scope for providing diverging customer requirements is very limited in this case. Effectively every customer gets exactly the same features and all you can do is allow them to maybe switch features off and on. The cost of developing a customisable experience with such a setup is high.

The great thing about this single application approach is its scalability. Each extra customer requires no extra development effort. It's the way billion dollar on-line businesses are made.

Few customers, Deep customisation

At the other extreme we might be selling an application to just a handful of clients. Each client has diverse requirements and is willing to spend the money to have them implemented. Do we really have a single application in this case or are we instead delivering bespoke solutions? Here the answer might be to have a common application library but separate codebases for each client. It would then follow that each client needs separate database schemas, databases and application instances.

In this scheme, the cost of developing divergent features is no greater than developing them for bespoke software. The pain comes in deciding what to code in your shared library and what to code in your bespoke applications. Getting that wrong can mean repeating the same code for each customer and seriously violating the DRY principle.

Because we are effectively building bespoke software, each extra customer requires development effort. As our business grows we have to hire more and more developers to service our customers. The problem is that development teams don't scale well and there are high organisational costs involved. Our profit on each instance of our product is likely to decline rather than rise. For this reason there's a limit to the size a company based on this development model can grow to.

So which one is Suteki Shop?

Suteki Shop only has one customer at present. So it's a single bespoke application based on that customer's requirements. In the new year I hope to add a second customer so I'll have to start making decisions about my multi-tenancy strategy. I want to make the right decisions now so that I can provision my third, fourth, tenth or one-hundredth as easily as possible. My customers are likely to have divergent requirements within the constraints of an eCommerce system. The easiest thing for my second customer would be to branch the code, make any changes they want and deploy a second database (with possibly a customised schema) and application instance. But that strategy will not scale to one-hundred customers. Let's make the assumption that I want to service maybe a hundred customers easily, but I'm unlikely to have more than a thousand. Consider the four axis of change above...

  1. Codebase. I really don't want to fork my codebase. I'm a firm believer in keeping things DRY. There will be no branching.
  2. Application Instance. This is less clear cut. There are obvious benefits from maintaining separate application instances for each customer and this was my initial intention. I can configure and deploy them as required. However, after looking at techniques for provisioning multiple divergent customers on one instance, I believe that the costs can be minimal. Having one instance is much easier to administer and monitor, so I am changing my mind on this point and will aim for one application instance. One application instance can of course be duplicated to scale over a server farm. I think this approach could scale to thousands of customers.
  3. Database schemas. In the old days, I would have said that having multiple schemas would cause intense scale pain, but that is changing with modern tools. Using an ORM allows us to defer schema creation until deployment. If I can automate the management of schema creation and change, then having multiple versions is less of a problem. Of course that all depends on having multiple databases....
  4. Database instances. If I chose to have one database instance I would have to have one schema which limits the amount of customisation I can do. I would also have to carefully key each customer's data, which is a development overhead. Automating database deployment and maintenance is relatively straightforward  so I think the choice here is clear cut. One database per customer. Also this gives me extremely simple database scalability, I can simply farm out my customers to multiple DBMS's. This approach will scale to hundreds of customers, thousands would be problematic but not impossible.

So now I've got my multi-tenancy strategy, how am I going to implement it? The core technology I'm going to use is component oriented software design using Dependency Injection and an IoC Container. I'm going to follow some of the recommendations that Oren Eini makes in these posts:

Multi Tenancy - Approaches and Applicability

Adaptive Domain Models with Rhino Commons

Components, Implementations and Contextual Decisions

Windsor - IHandlerSelector

Windsor - IModelInterceptersSelector

In part 2, I'm going to show my first faltering steps to making a single instance of Suteki Shop host more than one customer. Stay tuned!