NServiceBus for dummies who want to be smarties 6… sixth and last post in the series will show an example using the publish/subscribe functionality provided by NServiceBus.
One could image all kinds of cool integration scenarios where interested parties somehow receive events from our system regarding stuff that may be interesting to them. Let’s imagine that our user registration from the fifth post is an extremely interesting event to the sales department and the Über Boss of our enterprise.
To accomodate this, I create a separate assembly meant for holding messages that are not private to our application. This way, I can easily distinguish between messages that I can change whenever I feel like it, and messages that I must put more care and effort into crafting, because they will most likely live forever when a dozen applications in our enterprise are suddenly depending on them.
This means that my saga from the fifth post will handle an email confirmation like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public void Handle(ConfirmRegistration message) { Console.WriteLine("Confirming email {0}", Data.Email); NewUserService.CreateNewUserAccount(Data.Email); MailSender.Send(Data.Email, "Your registration request", "Your email has been confirmed, and your user account has been created"); Bus.Publish(new SomeoneActuallyRegistered {Email = Data.Email}); MarkAsComplete(); } |
As you can see, I Bus.Publish(...) a message saying that someone with a particular email has registered.
To be able to publish stuff, some kind of subscription storage must be configure for the endpoint. Therefore, I change my backend’s EndpointConfig to be configured with this:
1 2 3 4 5 6 |
Configure.With() .CastleWindsorBuilder(container) .Sagas() .NHibernateSagaPersisterWithSQLiteAndAutomaticSchemaGeneration() .MsmqSubscriptionStorage() .XmlSerializer(); |
– which means my backend will be storing subscriptions in a message queue.
If you are somehow unclear on the difference between sending and publishing, I can tell you that the conceptual difference is this: sending is like saying “do this stuff”, whereas publishing is like saying “this stuff happened” – see the difference in tense?
Functionally, the difference is that sending will put a message in the queue specified as recipient for that particular message whereafter one recipient will consume that message off its input queue (possibly competing with others), whereas publishing will put a message in the input queue of each one of whoever subscribed to that particular message (possibly many – possibly zero).
Now, to simulate the sales department and the über boss, I create two new projects containing an app.config that looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="MsmqTransportConfig" type="NServiceBus.Config.MsmqTransportConfig, NServiceBus.Core" /> <section name="UnicastBusConfig" type="NServiceBus.Config.UnicastBusConfig, NServiceBus.Core" /> </configSections> <MsmqTransportConfig InputQueue="salesDept" ErrorQueue="error" NumberOfWorkerThreads="1" MaxRetries="5" /> <UnicastBusConfig> <MessageEndpointMappings> <add Messages="PublicMessages" Endpoint="backend" /> </MessageEndpointMappings> </UnicastBusConfig> </configuration> |
– and similarly for the über boss.
Note how a subscriber must have a message endpoint mapping telling where messages will be published – in the example above, my mapping specifies that all messages from my PublicMessages assembly can be subscribe to on the endpoint named “backend”. And obviously, a subscriber must have an input queue, through which subscribed messages will be received.
The EndpointConfig can look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
namespace SalesDept { public class EndpointConfig : IConfigureThisEndpoint, AsA_Server, IWantCustomLogging, IWantToRunAtStartup { public IBus Bus { get; set; } public void Init() { } public void Run() { Bus.Subscribe<SomeoneActuallyRegistered>(); Console.WriteLine("Sales department running..."); } public void Stop() { } } } |
– and similarly for the über boss as well.
Note how the service subscribes to the messages that it wants to receive by calling Bus.Subscribe<...> in the Run() method. What happens, is that NServiceBus somehow communicates to whoever is configured as the publisher of messages of type SomeoneActuallyRegistered, that messages should be sent to this service’s input queue.
And then, I implement a message hander in each service… first, the sales department:
1 2 3 4 5 6 7 8 9 10 |
namespace SalesDept { public class SendInformationAboutProducts : IMessageHandler<SomeoneActuallyRegistered> { public void Handle(SomeoneActuallyRegistered message) { Console.WriteLine("Sending stupid emails to {0}", message.Email); } } } |
and then, the über boss:
1 2 3 4 5 6 7 8 9 10 |
namespace ÜberBoss { public class NoteEmailInLittleBlackBookOfEvilSchemes : IMessageHandler<SomeoneActuallyRegistered> { public void Handle(SomeoneActuallyRegistered message) { Console.WriteLine("Noting email {0}", message.Email); } } } |
To make this run, I go to “Set StartUp projects…” of my solution and make all my services start up when I press F5.
Now, we can check things out by pressing F5, thus running the backend, the sales department, and the über boss at once. On my machine, it looks like this:
Now it doesn’t take too much imagination to come up with all kinds of cool solutions to integration scenarios.
Conclusion
That concludes the sixth and last post in my “NServiceBus for dummies who want to be smarties” series. I hope the series can provide some information on how to get started using NServiceBus, and perhaps fill out some of the gaps that comes from documentation in the form of sparsely available blog posts from different sources.