Castle MicroKernel - What's in a Name?

Welcome back fellow Kernelites.  I've been blogging lately about a little framework I put together for implementing Model-View-Presenter in ASP.Net.  I have pointed out a few of the benefits of the framework, better separation of concerns, looser coupling between the View (web gui) and Presenters (logic orchestrator) and Services (data access, security, logging, instrumentation).  In this post I want to explore another sweet bonus the framework offers as a result of being built on top of the Castle MicroKernel (have I mentioned that I love the Kernel?)

Does this scenario sound familiar?

You have created a web application that you host on your systems and offer as a service to your customers.  This could be a blog enging, shopping cart, online catalog, bug tracking software, wiki, you name it.  You've really done a great job making it brandable and highly configurable on a per customer basis.  Brandable like colors and graphics, configurable like checkboxes to turn on/off certain features, customizable messages, backend database support etc.  Lets say the branding and customization is keyed off of the host header.  So, hankshardware.cart.com and billsbooks.cart.com all point to the same web application but display the proper branding and customizations accordingly.  Follow so far? 

Now your new customer for your shopping cart application tells you part of their business process is for the Purchase Order Number to be the Customers Phone Number.  Your Sales team has told you this is a deal breaker for the customer and that we have to do this (we won't go into the problem when Sales doesn't say no in this post).  So how are you going to make that happen?  If you said, "Well Bill, that completely depends on how the application was written"  you're exactly right!  Let's explore some possible scenarios.

1.  You crack open the Page/Control where the user would enter the Purchase Order and do something like this:

if (Request.ServerVariables["SERVER_NAME"].Equals("billsbooks.cart.com"))
{
order.PurchaseOrderNumber = txtPhoneNumber.Text;
}

This works right?  Sure, but remember my mantra "Say No to Kludge".  There are a few problems here.  First, you shouldn't be implementing a business rule in your View.  Second, even if you had this little tid bit somewhere in a business class, you are going to end up with 1,000 'if' statements for each one of your customers that "Must have this feature."

2.  You implement this as base product with some simple configuration switch like "Default Purchase Order Number to Phone Number".  You make the necessary changes to your configuration tool (UI, business objects, documentation, datastore) and you modify your application code to check this new configuration value and take the appropriate actions.

I would only take this route after doing some research with other existing customers or potential prospects.  If this is a feature that would benefit others I would consider adding it to base product.  But, you probably don't have that kind of time to evaluate each customization.

3.  Maybe you are in luck and your application is built on top of a rules system like BizTalk.  I've never used this product but I assume it could handle the scenario we are talking about.  I do know that BizTalk isn't cheap and it is probably overkill for the types of applications I'm using in this post.

4.  You've been listening to that crap Bill Pierce has been spewing about Castles and Kernels and Dependency Injection blah blah blah.  You have a nice little service called IOrderProcessor (or something similar) that is concerned with orchestrating the...order process...validating order data, updating inventory, updating the "Also purchased" system, queueing up a pack and ship request and firing off some confirmation emails (properly delegating to other services of course :).  You start the magic in your Presenter after the user clicks "Submit Order" in your View with something like:

// Shown for exmample only. 
// IOrderProcessor would normally be DI'd when the Presenter is constructed
IOrderProcessor orderProcessor = IoC.Resolve<IOrderProcessor>();
OrderConfirmation confirmation = null;
try
{
confirmation = orderProcessor.Process(order);
}
catch (OrderException oe)
{
// Explain the problem to the user
}
// Display the Confirmation

Dependency Injection gives us a lot of options for implementing our customization.  Wouldn't it be great if the code above would return BillsBooksOrderProcessor from the call to Resolve whenever someone was browsing billsbooks.cart.com?  That way we could decorate the existing IOrderProcessor and quickly set order.PurchaseOrderNumber = order.PhoneNumber before sending the order through the normal process.

I did some brief searching to see if Castle had this kind of capability.  Unfortunately what I found was when you request a component by type, it returns the first registered component that implements that type.  Other options would include requesting the component by key.  This was feasible but I didn't want to change the Presenter code to determine the proper key to use based on the Host Header.  So I did some further digging.  The Castle documentation is decent for standard uses and use cases.  What is the best resource for determing advance uses of open source projects?  The Unit Tests!  Same goes for other big open source projects like nHibernate.  I learn a lot about it just by perusing the unit tests that have been written.  To implement my madness I had to once again mess around under the hood of Castle.MicroKernel.

The beauty of the Kernel is the separation of concerns it implements under the covers.  The Kernel has several 'SubSystems' responsible for things like component Naming, configuration, conversion, and resource handling.  Each of these subsystems can be replaced as needed to enable different functionality.  I ended up creating a custom NamingSubSystem to implement our custom injection.

The NamingSubSystem is pretty simple.  It is responsible for organizing compenents in the Kernel by key and by service.  The key is specified by the developer through configuration or attributes when creating a Kernel.  The snippet below shows a configuration files that loads two components:

<componet id="default.OrderProcessor"
service="Cart.Interfaces.IOrderProcessor, Cart.Interfaces"
type="Cart.Services.OrderProcessor, Cart.Services" />
</components>
<componet id="billbooks.cart.com.OrderProcessor"
service="Cart.Interfaces.IOrderProcessor, Cart.Interfaces"
type="BillsBooks.Cart.Services.OrderProcessor, BillsBooks.Cart.Services" />

The 'id' corresponds to the Key that can be used to later retrieve the component from the Kernel.  The 'service' is the Interface implemented by the component and the 'type' is the concrete implementation of the service.  In the example above, both components implement IOrderProcessor.  The base product order processing is implemented in Cart.Services.OrderProcessor.  The customization we did for Bills Books is implemented in BillsBooks.Cart.Services.OrderProcessor.  The default NamingSubSystem in the Kernel would yield the following results:

// Returns Cart.Services.OrderProcessor because it was the first IOrderProcessor loaded
IOrderProcessor orderProcessor = IoC.Resolve<IOrderProcessor>();

// Returns BillsBooks.Cart.Services.OrderProcessor
IOrderProcessor orderProcessor = IoC.Resolve<IOrderProcessor>("billbooks.cart.com.OrderProcessor");

// Returns Cart.Services.OrderProcessor
IOrderProcessor orderProcessor = IoC.Resolve<IOrderProcessor>("default.OrderProcessor");

What we need is for the NamingSubSystem to dynamically determine which component we want.  So, I give you the DynamicNamingSubSystem:

using System;
using System.Collections;

using Castle.MicroKernel;
using Castle.MicroKernel.SubSystems.Naming;

namespace WCPierce.MicroKernel.SubSystems.Naming
{
public delegate string KeySearchCallback();

[Serializable]
public class DynamicNamingSubSystem : DefaultNamingSubSystem
{
protected IDictionary service2Keys;
protected KeySearchCallback keySearchCallback;

public DynamicNamingSubSystem()
: this(delegate() { return string.Empty; })
{
}

public DynamicNamingSubSystem(KeySearchCallback keySearchCallback)
: base()
{
if (keySearchCallback == null) throw new ArgumentNullException("keySearchCallback");

service2Keys = Hashtable.Synchronized(new Hashtable());
this.keySearchCallback = keySearchCallback;
}

public override void Register(string key, IHandler handler)
{
base.Register(key, handler);

Type service = handler.ComponentModel.Service;

ArrayList keys = null;
if (!service2Keys.Contains(service))
{
keys = new ArrayList();
keys.Add(key);
service2Keys[service] = keys;
}
else
{
keys = (ArrayList)service2Keys[service];
if( !keys.Contains(key))
{
keys.Add(key);
}
}
}

public override void UnRegister(string key)
{
IHandler handler = (IHandler)key2Handler[key];

base.UnRegister(key);

ArrayList keys = (ArrayList)service2Keys[handler.ComponentModel.Service];
keys.Remove(key);
}

public override void UnRegister(Type service)
{
base.UnRegister(service);
service2Keys.Remove(service);
}

public override IHandler GetHandler(Type service)
{
ArrayList keys = service2Keys[service] as ArrayList;

if (keys == null) return null;

if (keys.Count == 1) return base.GetHandler(service);

string keySearch = keySearchCallback() ?? string.Empty;

foreach (string key in keys)
{
if (key.IndexOf(keySearch, StringComparison.InvariantCultureIgnoreCase) >= 0 )
{
return GetHandler(key);
}
}

return base.GetHandler(service);
}
}
}

The DefaultNamingSubSystem stores each component key and service in a simple hash table.  We simply extend that by storing each service with an ArrayList of all the keys for compnents that implement that service.  What does that allow us to do?  When you instantiate the DefaultNamingSubSystem, you provide it with a simple callback that will be called each time you try to Resolve a component by Service.  So, even though you are asking for a Service you still have a chance to provide the key as well.  How does this help us implement our customizations on a per customer basis?  Consider this code:

public class MvpKernel : DefaultKernel
{
public MvpKernel()
: base()
{
ComponentModelBuilder.AddContributor(new ComponentActivatorInspector());
ComponentModelBuilder.AddContributor(new WebUserControlModelInspector());
}

protected override void RegisterSubSystems()
{
AddSubSystem(SubSystemConstants.ConfigurationStoreKey,
new DefaultConfigurationStore());

AddSubSystem(SubSystemConstants.ConversionManagerKey,
new DefaultConversionManager());

AddSubSystem(SubSystemConstants.NamingKey,
new DynamicNamingSubSystem(GetKeySearchString));

AddSubSystem(SubSystemConstants.ResourceKey,
new DefaultResourceSubSystem());
}

protected virtual string GetKeySearchString()
{
return String.Empty;
}
}

public class ShoppingCartKernel : MvpKernel
{
protected override string GetKeySearchString()
{
if (HttpContext.Current != null)
{
return HttpContext.Current.Request.ServerVariables["SERVER_NAME"] ?? string.Empty;
}

return string.Empty;
}
}

At this point you've either 1) Stopped reading, 2) Read everything but don't get it and don't care, 3) Read everyting everything but don't get it and do care, 4) Read the important parts and said "Oh Snap this is awesome".  Direct your attention please to the GetKeySeachString at the bottom of the code.  We now have the ability to tell the Kernel, "When I ask for a service, check to see if there is one with the host header in the key and give me that one instead of the default".  OHHH YEAHHH.  I'm giving orders to the Kernel now, that must make me the General (what a freakin' nerd).

Think of the injection possibilities now.  You could customize functionality based on host headears, a query string parameter, the currently logged in user, the current date, whatever.  Keep in mind that this is called each time a compenet is resolved so don't get too crazy with what you are doing in GetKeySearchString.  If there are no customizations loaded you shouldn't see a decrease in performance since it will revert to default functionality and take the first (only) component that implements the service. 

The next time you get a request to customize a little bit of functionality you can say, no problem.  Your time estimates will be measured in hours not days.  Sales is happy, the customer is happy because the cost of customization is decreased.  You are happy because you can keep each customers customizations isolated to their own assembly.  Plus when v.Next of your product comes out, you can evaluate customizations and easily incorporate the pertinent ones into base product.  Champagne will fall from the heavens and you can retire at 25 and spend your weekends writing long blog posts about obscure hacks to open source projects.  DISCLAIMER: Actual results may vary.

UPDATED

Oren kindly pointed out the fact that I'm not half as smart as I think I am :)  If you mark your component as a Singleton, the Kernel will only create one instance the first time it is requested.  This can be problematic for the DynamicNamingSubSystem in the following scenario described by Orin:  You have a singleton, Billing, that has as a property another Singleton, OrderProcessor.  Let's say the first time a user comes to the app is through billsbooks.cart.com.  The Kernel will create the Billing Singleton and resolve all of its dependencies (like the OrderProcessor) and use our custom BillsBooksOrderProcessor.  The next person that accesses the site through hankshardware.cart.com will ask for the Billing object.  Since it is a Singleton, the Kernel will return the instance that has already been created, which uses the BillsBooksOrderProcessor.  DOH!  So our options are:

1.  Ensure that any customizations of Singletons take place at the top level.  Top level meaning that the Singleton is not a property of any other Singleton.  I don't like this because it reduces your ability to easily refactor as needed.  The developer now needs to remember that if I want to customize component A I also need to customize component B.  Not good.

2.  Use Transient or PerWebRequest instead of Singleton.  This could significantly bump up memory usage for your site because each time somebody needs to do Billing you will get a new object instance.  The whole purpose of a Singleton is to prevent the creation of unnecessary objects.

3.  Only use the DynamicNamingSubSystem for your Transient/PerWebRequest objects like Presenters and Views (Create a standard Kernel with a child Kernel that uses the DynamicNamingSubSystem).  This still allows you to dynmically change user interface elements at least.

4.  I'll dive back under the covers of Castle.MicroKernel but I'm not seeing the light at the end of the tunnel.  For Singletons I'm thinking I'd have to walk the entire object graph to make sure you are really getting the properly configured Singleton with the right customizations.  This could result in a large number of permutations. YUCKY!

posted @ Saturday, September 30, 2006 6:48 AM


Print

Comments on this entry:

# re: Castle MicroKernel - What's in a Name?

Left by Ayende Rahien at 9/30/2006 7:29 AM

Interesting appraoch.
But what happens when you use singleton components (the default)?

Assume that I have

IBilling
- IOrderProcessor.

You have customized only IOrderProcessor.

Now, first request comes from bills books, and then you get:

DefaultBilling ( BillsBookOrderProcessor)

Next request, from a different customer, you get the _same_ instnace that you gotten before, again with BillsBookOrderProcessor, which is the worng proecssor for this case.

# re: Castle MicroKernel - What's in a Name?

Left by hammett at 10/1/2006 6:32 AM

Good point, Ayende. I'd make the DefaultBilling transient. I think this will solve it.

Bill, are the billing and order processor stateless? If not, they must be transient classes anyway.

# re: Castle MicroKernel - What's in a Name?

Left by Alex Maitland at 10/15/2006 6:43 PM

I'm not expert on the subject, so feel free to tell me that I'm completely wrong.

What if you created another interface called IOrderProcessorFactory, implement the logic of loading the appropriate IOrderProcessor in the concrete version and still having those instances of IOrderProcessor marked as singletons. You can still use dependency injection by passing IOrderProcessorFactory. IOrderProcessorFactory can still use windsor to load those versions by their concrete types.

I know it's not a perfect solution and it creates yet another layer of indirection. It does however keep things loosely coupled.

I'm really just interested to hear your comments on the subject.

# re: Castle MicroKernel - What's in a Name?

Left by Bill Pierce at 10/19/2006 4:15 AM

Alex,
Sounds perfeclty valid and I believe you could pull it off without too much effort. The only side effect is now you would have one factory per service, responsible for loading the correct instance of the service.

-Bill

Your comment:



 (will not be displayed)


 
 
 
Please add 1 and 3 and type the answer here:
 

Live Comment Preview:

 
«August»
SunMonTueWedThuFriSat
272829303112
3456789
10111213141516
17181920212223
24252627282930
31123456