Once upon a time I wrote a blog post about why Service Locator is an anti-pattern, and ever since then, I occasionally receive rebuffs from people who agree with me in principle, but think that, still: in various special cases (the argument goes), Service Locator does have its uses.
Most of these arguments actually stem from mistaking the mechanics for the role of a Service Locator. Still, once in a while a compelling argument seems to come my way. One of the most insistent arguments concerns message dispatching – a pattern which is currently gaining in prominence due to the increasing popularity of CQRS, Domain Events and kindred architectural styles.
In this article I’ll first provide a quick sketch of the scenario, followed by a typical implementation based on a ‘Service Locator’, and then conclude by demonstrating why a Service Locator isn’t necessary.
Scenario: Message Dispatching
Appropriate use of message dispatching internally in an application can significantly help decouple the code and make roles explicit. A common implementation utilizes a messaging interface like this one:
public interface IChannel
{
void Send<T>(T message);
}
Personally, I find that the generic typing of the Send method is entirely redundant (not to mention heavily reminiscent of the shape of a Service Locator), but it’s very common and not particularly important right now (but more about that later).
An application might use the IChannel interface like this:
var registerUser = new RegisterUserCommand(
Guid.NewGuid(),
"Jane Doe",
"password",
"jane@ploeh.dk");
this.channel.Send(registerUser);
// ...
var changeUserName = new ChangeUserNameCommand(
registerUser.UserId,
"Jane Ploeh");
this.channel.Send(changeUserName);
var resetPassword = new ResetPasswordCommand(
registerUser.UserId);
this.channel.Send(resetPassword);
Obviously, in this example, the channel variable is an injected instance of the IChannel interface.
On the receiving end, these messages must be dispatched to appropriate consumers, which must all implement this interface:
public interface IConsumer<T>
void Consume(T message);
Thus, each of the command messages in the example have a corresponding consumer:
public class RegisterUserConsumer : IConsumer<RegisterUserCommand>
public class ChangeUserNameConsumer : IConsumer<ChangeUserNameCommand>
public class ResetPasswordConsumer : IConsumer<ResetPasswordCommand>
This certainly is a very powerful pattern, so it’s often used as an argument to prove that Service Locator is, after all, not an anti-pattern.
Message Dispatching using a DI Container
In order to implement IChannel it’s necessary to match messages to their appropriate consumers. One easy way to do this is by employing a DI Container. Here’s an example that uses Autofac to implement IChannel, but any other container would do as well:
private class AutofacChannel : IChannel
private readonly IComponentContext container;
public AutofacChannel(IComponentContext container)
if (container == null)
throw new ArgumentNullException("container");
this.container = container;
public void Send<T>(T message)
var consumer = this.container.Resolve<IConsumer<T>>();
consumer.Consume(message);
This class is an Adapter from Autofac’s IComponentContext interface to the IChannel interface. At this point I can always see the “Q.E.D.” around the corner: “look! Service Locator isn’t an anti-pattern after all! I’d like to see you implement IChannel without a Service Locator.”
While I’ll do the latter in just a moment, I’d like to dwell on the DI Container-based implementation for a moment.
While AutofacChannel uses Autofac (a DI Container) to implement the functionality, it’s not (necessarily) a Service Locator in action. This was the point I already tried to get across in my previous post about the subject: just because its mechanics look like Service Locator it doesn’t mean that it is one. In my implementation, the AutofacChannel class is a piece of pure infrastructure code. I even made it a private nested class in my Composition Root to underscore the point. The container is still not available to the application code, so is never used in the Service Locator role.
One of the shortcomings about the above implementations is that it provides no fallback mechanism. What happens if the container can’t resolve the matching consumer? Perhaps there isn’t a consumer for the message. That’s entirely possible because there are no safeguards in place to ensure that there’s a consumer for every possibly message.
The shape of the Send method enables the client to send any conceivable message type, and the code still compiles even if no consumer exists. That may look like a problem, but is actually an important insight into implementing an alternative IChannel class.
Message Dispatching using weakly typed matching
Consider the IChannel.Send method once again:
Despite its generic signature it’s important to realize that this is, in fact, a weakly typed method (at least when used with type inferencing, as in the above example). Equivalently to a bona fide Service Locator, it’s possible for a developer to define a new class (Foo) and send it – and the code still compiles:
this.channel.Send(new Foo());
However, at run-time, this will fail because there’s no matching consumer. Despite the generic signature of the Send method, it contains no type safety. This insight can be used to implement IChannel without a DI Container.
Before I go on I should point out that I don’t consider the following solution intrinsically superior to using a DI Container. However, readers of my book will know that I consider it a very illuminating exercise to try to implement everything with Poor Man’s DI once in a while. Using Poor Man’s DI often helps unearth some important design elements of DI because it helps to think about solutions in terms of patterns and principles instead of in terms of technology. However, once I have arrived at an appropriate conclusion while considering Poor Man’s DI, I still tend to prefer mapping it back to an implementation that involves a DI Container. Thus, the purpose of this section is first and foremost to outline how message dispatching can be implemented without relying on a Service Locator.
Before I go on I should point out that I don’t consider the following solution intrinsically superior to using a DI Container. However, readers of my book will know that I consider it a very illuminating exercise to try to implement everything with Poor Man’s DI once in a while.
Using Poor Man’s DI often helps unearth some important design elements of DI because it helps to think about solutions in terms of patterns and principles instead of in terms of technology.
However, once I have arrived at an appropriate conclusion while considering Poor Man’s DI, I still tend to prefer mapping it back to an implementation that involves a DI Container.
Thus, the purpose of this section is first and foremost to outline how message dispatching can be implemented without relying on a Service Locator.
While this alternative implementation isn’t allowed to change any of the existing API, it’s a pure implementation detail to encapsulate the insight about the weakly typed nature of IChannel into a similarly weakly typed consumer interface:
private interface IConsumer
void Consume(object message);
Notice that this is a private nested interface of my Poor Man’s DI Composition Root – it’s a pure implementation detail. However, given this private interface, it’s now possible to implement IChannel like this:
private class PoorMansChannel : IChannel
private readonly IEnumerable<IConsumer> consumers;
public PoorMansChannel(params IConsumer[] consumers)
this.consumers = consumers;
foreach (var c in this.consumers)
c.Consume(message);
Notice that this is another private nested type that belongs to the Composition Root. It loops though all injected consumers, so it’s up to each consumer to decide whether or not to do anything about the message.
A final private nested class bridges the generically typed world with the weakly typed world:
private class Consumer<T> : IConsumer
private readonly IConsumer<T> consumer;
public Consumer(IConsumer<T> consumer)
this.consumer = consumer;
public void Consume(object message)
if (message is T)
this.consumer.Consume((T)message);
This generic class is another Adapter – this time adapting the generic IConsumer<T> interface to the weakly typed (private) IConsumer interface. Notice that it only delegates the message to the adapted consumer if the type of the message matches the consumer.
Each implementer of IConsumer<T> can be wrapped in the (private) Consumer<T> class and injected into the PoorMansChannel class:
var channel = new PoorMansChannel(
new Consumer<ChangeUserNameCommand>(
new ChangeUserNameConsumer(store)),
new Consumer<RegisterUserCommand>(
new RegisterUserConsumer(store)),
new Consumer<ResetPasswordCommand>(
new ResetPasswordConsumer(store)));
So there you have it: type-based message dispatching without a DI Container in sight. However, it would be easy to use convention-based configuration to scan an assembly and register all IConsumer<T> implementations and wrap them in Consumer<T> instances and use this list to compose a PoorMansChannel instance. However, I will leave this as an exercise to the reader (or a later blog post).
My claim still stands
In conclusion, I find that I can still defend my original claim: Service Locator is an anti-pattern.
That claim, by the way, is falsifiable, so I do appreciate that people take it seriously enough by attempting to disprove it. However, until now, I’ve yet to be presented with a scenario where I couldn’t come up with a better solution that didn’t involve a Service Locator.
Keep in mind that a Service Locator is defined by the role it plays – not the shape of the API.
Remember Me
a@href@title, b, em, i, strike, strong
Page rendered at Monday, May 21, 2012 4:35:12 AM (Romance Daylight Time, UTC+02:00)
Disclaimer The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.