One of the questions that came up from my NServiceBus – .NET Service Bus Smackdown post was about the Polymorphic Message Dispatch and Polymorphic Message Routing features. People wanted to know what those are, why they’re important, and if other technologies (specifically WCF and BizTalk) support them.
Messaging Basics
First of all, when building a system using messaging, you don’t have methods that are invoked on some remote object (a.k.a “service”) to which you pass parameters. Instead, you use some generic piece of infrastructure (in the world of Java, this is most commonly a Message Broker) to send a message where a message can be thought of as a serializable class. Here’s an example of a message:
public class UserCreated : IMessage { public Guid UserId { get; set; } public string Name { get; set; } }
This message would be published using NServiceBus like this:
bus.Publish<UserCreated>( m =>
{
m.UserId = Guid.NewGuid();
m.Name = "John Smith";
});
This can be contrasted with RPC models like WCF where you need to define a “service” that has methods on it, where those methods accepts parameters. Sometimes developers try to make this more generic by having a single “service” with one method on it named something like “Process” where the parameter it accepts is of the type “object”, or they introduce generics like this: Process<T>(T message);
This is where Polymorphic Message Dispatch comes in:
Polymorphic Message Dispatch
While you can pull of the WCF generics thing, one thing that is more difficult (without writing your own dispatch model) is to have a pipeline of classes which can be invoked based on their relationship to the type passed in. Using NServiceBus, both of the following message handlers will be invoked when UserCreated arrives:
public class Persistence : IHandleMessages<UserCreated> { public void Handle(UserCreated message) { } } public class Audit : IHandleMessages<IMessage> { public void Handle(IMessage message) { } }
Now some might say that WCF, BizTalk, and the .NET Service Bus allow you to do auditing in their own internal pipeline, and that’s true. The place where this becomes more powerful is when you need to build V2 of your system, and the publisher now publishes a slightly different event – that a user was created as a part of a campaign, requiring the subscriber to register statistics about the campaign. Of course, this event also means that a user was created. Here’s how you’d do that with NServiceBus:
public class UserCreatedFromCampaign : UserCreated { public Guid CampaignId { get; set; } } //publisher code bus.Publish<UserCreatedFromCampaign>( m => { m.UserId = Guid.NewGuid(); m.Name = "John Smith"; m.CampaignId = theCampaignId; } //subscriber code public class Statistics : IHandleMessages<UserCreatedFromCampaign> { public void Handle(UserCreatedFromCampaign message) { } }
The important part is what you don’t see – since UserCreatedFromCampaign inherits from UserCrated, the Persistence handler we had from V1 will also be invoked, and so will the Audit handler of course. You don’t have to make your new code call the old code like you would in a method based dispatch model. This makes sure that the coupling in your service layer code remains constant over time as you grow the functionality of your system.
This was one of the main benefits mentioned by Rackspace in their use of NServiceBus (here):
“The main benefit NServiceBus has brought us so far is developer scalability due to lower coupling and higher consistency in our code.”
But, when looking at the above scenario, we can obviously expect that all sorts of things can happen in relation to campaigns – it is a separate concern, and thus should be handled by a separate subscriber. And this bring us to…
Polymorphic Message Routing
The challenge that we have here is that we no longer have a hierarchy where something clearly belongs on top of something else. We have users created and activities happening related to campaigns – that may happen in any combination. By having separate subscribers, we could then introduce new handlers/subscribers to our environment without touching or taking down any of the other subscribers. Here’s what the subscribers would look like:
public class Persistence : IHandleMessages<UserCreated> { public void Handle(UserCreated message) { } } public class Statistics : IHandleMessages<CampaignActivityOccurred> { public void Handle(CampaignActivityOccurred message) { } }
But if each of the above messages were a class, how could we define a message which inherited from both?
Before answering that, we need to understand why the publisher wouldn’t just publish both of the above messages. You see, the publisher can’t make any assumptions about its subscribers – it could be that one of them has logic that correlates across both of these messages that could end up counting the occurrence as happening twice rather than once, possibly charging the account associated with the campaign twice. Publishing two messages results in two transactions when there really should have been one.
So, here’s how to define messages so that we can have multiple inheritance:
public interface UserCreated : IMessage { Guid UserId { get; set; } string Name { get; set; } } public interface CampaignActivityOccurred : IMessage { Guid CampaignId { get; set; } Guid ActivityId { get; set; } } public interface UserCreatedFromCampaign : UserCreated, CampaignActivityOccurred { }
And when the publisher publishes UserCreatedFromCampaign, the event would be routed to both the UserCreated subscriber and the CampaignActivityOccurred subscriber. The power of this approach is felt as we handle new requirements around purchases made related to a campaign. Now we can have another event which inherits from CampaignActivityOccurred and not have to worry since the existing subscriber will be routed those messages automatically.
Since WCF doesn’t have publish/subscribe capabilities, we might as well move along.
Not to throw a burning match on an ocean of oil, but REST doesn’t really support this either.
Not Content-Based Routing
This may sound like the content-based router pattern from EIP (CBR), but it’s not. The important difference is that there isn’t some part of the routing that depends on the structure of the messages. The major drawback of CBR is that it creates a central place in your system that needs to be changed any time *syntactic* changes happen to message structure *in addition to* to changes in the subscribers.
Now, this is where the BizTalk guys would say that “that’s why we can do message transformations”, and then the subscribers wouldn’t need to be changed. However, can we really know when getting a requirement that the change is syntactic and not semantic? I mean, it’s quite common that changes to message structures happen together with changes to processing logic.
You may be beginning to get the feeling that more and more logic is being sucked out of the subscribers into some monolithic black hole that is likely going to be unmaintainable and quite slow.
This is one of the main differences between using a bus and a broker – a bus supports the correct distribution of logic keeping the system loosely coupled; brokers are useful integration engines when you absolutely can’t change the applications being integrated. Enterprise Application Integration (EAI) brokers don’t usually make good Enterprise Service Bus (ESB) technology.
In Closing
NServiceBus has all sorts of features you didn’t know you needed until you saw what life could be like when you had them. Most of these features don’t have snazzy drag-and-drop demos that make people ooh-and-aah and TechEds and PDCs, but they’re really necessary to avoid finding yourself in yet another big-ball-of-mud code base telling your manager/customer (again) that it would be faster to rebuild the system from scratch than to implement that new requirement in the old one.
Take NServiceBus for a spin and see for yourself.