Handling Multitenancy with Castle Windsor – Part 2 (Domain Events)

In a previous posting, I talked about how Windsor can select a particular implementation depending on some criteria we are interested in such as who the current customer is. This is done through the IHandlerSelector interface which is used in the call to the Resolve method on the container. All well and good.

One of the techniques I like to use in my applications is the Domain Event pattern which I’d recommend you read if you’re not familliar with it. It’s essentially a nice way to keep our code loosely coupled. When the domain raises an event as the result of some state change it is ultimately resolved by an IoC container, in my case, Windsor. However, an event can have multiple handlers which means that in order to return all the handlers that we want to invoke for our event we must call the ResolveAll method on the container.

Thinking about multitenancy again and our PlaceOrder example, imagine that the request to place an order is successful and our domain has raised a OrderWasPlaced event. We need to inform the customer that the order was received and is being processed. However, CustomerA wishes to be informed by email whilst CustomerB prefers text messages on mobiles. Wouldn’t it be great if we didn’t have to care about this and could just rely on the fact that when the event is raised the appropriate handlers would be invoked for the current customer?

Sounds like IHandlerSelector is ideal for this but unfortunately this interface is only used in the call to Resolve because the SelectHandler method only returns a single handler:

public IHandler SelectHandler(string key, Type service, IHandler[] handlers)
{
    var user = HttpContext.Current.User;

    if(user.IsInRole("SomeRoleThatIdentifiesCompanyA"))
        return handlers.Where(x => x.ComponentModel
           .Implementation == typeof (CompanyAController)).First();

        return handlers.Where(x => x.ComponentModel
               .Implementation == typeof(CompanyBController)).First();
}

At least, that’s the case in the current release of Windsor (2.5.3). Already, in the latest builds though, is a new interface IHandlersFilter, which returns an array of IHandler which we could implement like this:

public class OrderWasPlacedHandlersFilter : IHandlersFilter
{
    public bool HasOpinionAbout(Type service)
    {
        return service == typeof(IHandle<OrderWasPlaced>);
    }

    public IHandler[] SelectHandlers(Type service, IHandler[] handlers)
    {
        var user = HttpContext.Current.User;

        if(user.IsInRole("SomeRoleThatIdentifiesCompanyA"))
            return handlers.Where(x => x.ComponentModel
                           .Implementation.Name.Contains("CompanyA")).ToArray();

        return handlers.Where(x => x.ComponentModel
                       .Implementation.Name.Contains("CompanyB")).ToArray();
    }
}

Notice that in the SelectHandlers method (plural) we are returning an array of IHandler not just the first one as in the previous example. That’s really the only difference between the two. IHandlerSelector is used in the call to Resolve whilst IHandlersFilter is used in the call to ResolveAll. There is a downside in that because the HasOpinionAbout method expresses an interest in handlers for the event, the SelectHandlers method would return to us all the event handlers implementing this interface regardless of who the current customer is so we have to do some additional filtering which I’ve chosen to do here by looking for the class name of the handler to include that of the company, e.g. CompanyAOrderWasPlacedHandler, which admittedly, is a little bit fragile, especially if we were to rename our classes but tools like Resharper can mitigate the risk there a little bit.

The last remaining thing to do is register our filter implementation with Windsor and we’re good to go:

WindsorContainer _container = new WindsorContainer();
_container.Kernel.AddHandlersFilter(new OrderWasPlacedHandlersFilter());

With the addition of the IHandlersFilter interface to Windsor we now have in place an end-to-end solution for multitenant applications. We can perform actions for a particular customer by influencing the Resolve method and now we can invoke specific event handlers for that same customer by influencing the ResolveAll method whilst at the same time keeping the service layer free of any responsibility to determine who and what to call.

Advertisements
Handling Multitenancy with Castle Windsor – Part 2 (Domain Events)

2 thoughts on “Handling Multitenancy with Castle Windsor – Part 2 (Domain Events)

  1. Matthew Nichols says:

    Good articles on multitenacy (a topic near and dear to my heart) and Dependency Injection. Do you know whether any other containers give similar opportunities to customize the resolution of components as you describe in Windsor?

    1. Hi Matthew,

      Thanks for the comment. Unfortunately I don’t. I’ve stuck with Windsor for a long time now and so my knowledge of other containers is fairly limited.

Comments are closed.