Message Dispatching without Service Location by Mark Seemann
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.
- Is it simple? Yes.
- Is it flexible? Yes, although it has shortcomings.
- Would I use it like this? Perhaps. It depends :)
- Is it the only way to implement IChannel? No - see the next section.
- Does it use a Service Locator? No.
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:
void Send<T>(T message);
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.
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; } public void Send<T>(T message) { 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.
Comments
I'm helping a .NET vendor improve their blog by finding respected developers who will contribute guest posts. Each post will include your byline, URL, book link (with your Amazon affiliate link) and a small honorarium. It can either be a new post or one of your popular older posts.
Being an author myself, I know that getting in front of new audiences boosts sales, generates consulting opportunities and in this case, a little cash. Would you be interested? If so, let me know and I'll set you up.
Cheers,
Bob Walsh
Looking forward to getting your book later this month.
I think it comes down to the definition of Service Locator. I'm not sure that the AutofacChannel example is much different than a common example I come up against, which is a ViewModel factory that more or less wraps a call to the container, and then gets injected as a IViewModelFactory into classes that need to create VMs. I don't feel that this is "wrong," as I only allow this kind of thing in cases where more explicit injection is significantly more painful. However I do still think of it as Service Location, and it does violate the Three Calls Pattern. As long as I limit the number of places this is allowed and everyone is aware of them, I see little risk in doing it this way. Some might argue it's a slippery slope . . .
Whether it is a static dependency or an injected instance, it seems unnatural to me to call a service directly from a domain object. I think I've seen you say the same thing, but I'm not sure in what context.
Anyway, was wondering if you had any additional thoughts on the subject. I've been struggling with it for a while, and have settled (temporarily) on firing events outside of the domain object (i.e. call the domain.Method, then fire the event from the command handler).
Thanks for writing.
It's my experience that in MVVM one definitely needs some kind of factory to create ViewModels. However, there's no reason to define it as a Service Locator. A normal (generic) Abstract Factory is preferable, and achieves the same thing.
Regarding the question about whether or not to raise Domain Events from within Entities: it bothered me for a while until I realized that when you move towards CQRS and Event Sourcing, Commands and Events become first-class citizens and this problem tends to go away because you can keep the logic about which commands raises which events decoupled from the Entities. In this architectural style, Entities are immutable, so nothing ever changes within them.
In CQRS we have consumers that consume Commands and Events, and typically we have a single consumer which is responsible for receiving a Command and (upon validation) convert it to an Event. Such a consumer is a Service which can easily hold other Services, such as a channel upon which Events can be published.
Thanks.
I had that problem previously, but I thought I fixed it months ago. From where I'm sitting, the code looks OK both in Google Reader on the Web, Google Reader on Android as well as FeedDemon. Can you share exactly where and how it looks unreadable?
http://imgur.com/LvCfJ
Thanks.
Thanks.
Why I am posting:
I am currently designing the architecture of a new application and I want to design my domain models persistent ignorant but still use them directly in the NHibernate mapping to benefit from lazy loading and to not have near identical entity objects. One part of a PI domain model is that the models might rely on services to do their work which get injected using constructor injection. Now, NHibernate needs a default constructor by default but that can be changed (see: http://fabiomaulo.blogspot.com/2008/11/entities-behavior-injection.html). In the middle of this post there is a class implementation called ReflectionOptimizer that is responsible for creating the entities. It uses an injected container to receive an instance of the requested entity type or falls back to the default implementation of NHibernate if that type is unknown to the container.
Do you think this is using the container in a service locator role?
I think not, because a Poor Man's DI implementation of this class would get a list of factories, one for each supported entity and all of this is pure infrastructure.
The biggest benefit of changing the implementation in a way that it receives factories is that it fails fast: I am constructing all factories along with their dependencies in the composition root.
What is your view on this matter?
It'd be particularly clean if you could inject an Abstract Factory into your NHibernate infrastructure and keep the container itself isolated to the Composition Root. In any case I agree that this sounds like pure infrastructure code, so it doesn't sound like Service Location.
However, I'd think twice about injecting Services into Entities - see also this post from Miško Hevery.
If Message Bus is used - how is about Layers? Should it be some kind of "Superlayer" (visible for all other layers?)
How do you think - in which situation should Message Bus be involved?
Should it better be implemented or is there some good products? (C#, not commerce licence)
It's not too hard to implement a message bus on top of a queue system, but it might be worth taking a look at NServiceBus or Rebus.
I have implemented this pattern before and everything is well when the return type is void.
So, for dispatching messages, this is a really usefull and flexible pattern
However, I was looking into implementing the same thing for a query dispatcher. The structure is similar, with the difference that your messages are queries and that the consumer actually returns a result.
I do have a working implementation but I cannot get type inference to work on my query dispatcher. That means that every time I call the query dispatcher I need to specify the return type and the query type
This may seem a bit abstract, but you can check out it this question on StackoverFlow: type inference with interfaces and generic constraints.
I'm aware that the way I'm doing it there is not possible with c#, but I was wondering if you'd see a pattern that would allow me to do that.
What is your view on this matter?
Many thanks!
Kenneth, thank you for writing. Your question reminded my of my series about Role Hints, particularly Metadata, Role Interface, and Partial Type Name Role Hints. The examples in those posts aren't generically typed, but I wonder if you might still find those patterns useful?
Hi Mark,
I am also using this pattern as an in-process mediator for commands, queries and events. My mediator (channel in your case) now uses a dependency resolver, which is a pure wrapper around a DI container and only contains resolve methods.
I am now trying to refactor the dependency resolver away by creating separate factories for the command, query and event handlers (in your example this are the consumers). In my current code and also in yours and dozens of other implementations on the net don’t deal with releasing the handlers. My question is should this be a responsibility of the mediator (channel)? I think so because the mediator is the only place that knows about the existence of the handler. The problem I have with my answer is that the release of the handler is always called after a dispatch (send) even though the handler could be used again for sending another command of the same type during the same request (HTTP request for example). This implies that the handler’s lifetime is per HTTP request.
Maybe I am thinking in the wrong direction but I would like to hear your opinion about the releasing handler’s problem.
Many thanks in advance,
Martijn Burgers
Martijn, thank you for writing. The golden rule for decommissioning is that the object responsible for composing the object graph should also be responsible for releasing it. That's what some DI Containers (Castle Windsor, Autofac, MEF) do - typically using a Release method. The reason for that is that only the Composer knows if there are any nodes in the object graph that should be disposed of, so only the Composer can properly decommission an object graph.
You can also implement that Resolve/Release pattern using Pure DI. If you're writing a framework, you may need to define an Abstract Factory with an associated Release method, but otherwise, you can just release the graph when you're done with it.
In the present article, you're correct that I haven't addressed the lifetime management aspect. The Composer here is the last code snippet that composes the PoorMansChannel object. As shown here, the entire object graph has the same lifetime as the PoorMansChannel object, but I don't describe whether or not it's a Singleton, Per Graph, Transient, or something else. However, the code that creates the PoorMansChannel object should also be the code that releases it, if that's required.
In my book's chapter 8, I cover decommissioning in much more details, although I don't cover this particular example. I hope this answer helps; otherwise, please write again.