9 Oct
2009

Exploring NServiceBus

I’ve been learning a bit more about Service-Oriented Architecture (SOA) and Event-Drive Architecture (EDA) over the last couple of months. Something that kept coming back in the articles and books I’ve read so far is the concept of an Enterprise Service Bus (ESB). I’ve heard numerous times about NServiceBus, Mass Transit and Rhino Service Bus in the past, but I’ve never fully realized what types of problems these technologies try to solve. Besides trying to learn about the scenarios and business needs that drive these technologies, I also decided to take a closer look at NServiceBus by creating the simplest example I could think of: Starbucks!

You’ve probably found out by now that yours truly isn’t capable of making up innovative sample applications, but since we actually have two (!) Starbucks shops already here in Belgium, I couldn’t let this go unnoticed either.

The scenario is actually quite simple:

  1. The customer places his order at the cashier.
  2. The cashier passes the order to a barista who starts preparing the drinks.
  3. The cashier asks the customer to pay for his order.
  4. When the customer paid for his order at the cashier, the barista is informed that the payment has been completed.
  5. When the barista finishes its order, he checks whether payment has been completed and delivers the order to the customer.

I’ve used NSB 2.0 for this exercise, which is the trunk version at the moment. The thing that amazed me the most was how easy it is to get started. I pretty much expected to be muddling around for a week before I could actually send my first message, but this was certainly not the case. Let me show a couple of basic code snippets that illustrate how NSB is used.

Here’s an example of the fluent API for bootstrapping NSB:

Configure.With()
    .StructureMapBuilder(ObjectFactory.Container)
    .MsmqSubscriptionStorage()
    .XmlSerializer()
    .Sagas()
    .NHibernateSagaPersisterWithSQLiteAndAutomaticSchemaGeneration()
    .MsmqTransport()
        .IsTransactional(true)
        .PurgeOnStartup(false)
    .UnicastBus()
        .ImpersonateSender(false)
        .LoadMessageHandlers()
    .CreateBus()
    .Start();

Notice that I’m able to reuse my IoC container of choice that is also used elsewhere in the application. After bootstrapping NSB, the IoC container contains an instance of IBus which we can use to send and publish messages, etc. …

A message that can be sent with NSB must implement the IMessage interface.

[Serializable]
public class NewOrderMessage : IMessage
{
    ...
}

Notice that NSB doesn’t require a message to be a class. You can just as well use an interface:

public interface INewOrderMessage : IMessage
{
    ...
}

A message can be send using an instance of the bus:

_bus.Send(new NewOrderMessage(...));

Handling a message is accomplished using a message handler that implements the IHandleMessages interface:

public class CashierMessageHandler 
    : IHandleMessages<NewOrderMessage>
{
    public void Handle(NewOrderMessage message)
    {
        ...
    }    
}

The destination of a message must be configured in the configuration file of the sending application.

<UnicastBusConfig>
    <MessageEndpointMappings>
        <add Messages="CashierContracts" Endpoint="cashier"/>
    </MessageEndpointMappings>
</UnicastBusConfig>

When publishing a particular message, this kind of configuration goes in the configuration file of the subscribing application instead of the publisher.

Bus.Publish(new PaymentCompleteMessage(...));
<UnicastBusConfig>
    <MessageEndpointMappings>
        <add Messages="CashierContracts.PaymentCompleteMessage, 
                       CashierContracts" 
             Endpoint="cashier"/>
    </MessageEndpointMappings>
</UnicastBusConfig>

NSB also supports sagas. Here’s an example of how to create a saga.

public class CashierMessageHandler 
    : Saga<CashierSagaData>,
      IAmStartedByMessages<NewOrderMessage>,
      IHandleMessages<PaymentMessage>
{
    public void Handle(NewOrderMessage message)
    {
        ...
    }
    
    public void Handle(PaymentMessage message)
    {
        ...
    }
}

public class CashierSagaData : IContainSagaData
{
    public virtual Guid Id { get; set; }
    public virtual String Originator { get; set; }
    public virtual String OriginalMessageId { get; set; }

    ...
}

The saga data class contains data that should be persisted during the lifetime of a saga so that it can be used when different messages arrive. So make sure that the public members of saga data classes are virtual!

You can find the code of this sample application using NServiceBus at the Elegant Code repository (turns out we actually have one of those at Google Code). I’ve probably did a whole bunch of things wrong with it, but I definitely learned a thing or two in the process :-).

There’s a lot more that I want to learn about NSB like testing sagas, the generic host, etc. …

Let me round of this post by providing a couple of resources that were very helpful:

I just want to say thanks to Andreas for answering my stupid questions on the NServiceBus user group.

Till next time,

13 thoughts on “Exploring NServiceBus

  1. Great post. I need to test NServiceBus myself. It would also be cool if you could explain all the lines in the fluent configuration πŸ™‚ Guess there is alot of stuff built into each of them.

  2. Can we use this to connect 2 computer behind the router I.e. Remote computers not having fixed ip.

  3. This is an excellent example for getting started with NServicebus and from my point of view should be contained in the official samples of the NServicebus package!

  4. Hi ! Great post ! I downloaded the example and can’t figure out what’s wrong with it.

    Casher receives 4 times message of payment in method
    public void Handle(PaymentMessage message)

    And the method within Barista is never called
    public void Handle(PaymentCompleteMessage message)

    And customer is not notified of OrderReady state !!!
    What’s the problem ?
    Thanks !

  5. Jan Van Ryswyck :@EugenAre you running the timeout manager?

    I figured out why the messages weere repeated more times. It was because of MaxRetries=”5″ parameter.
    After futher investigation we found out that the customer wasn’t receiveing the answer because of
    the following.
    We commented line MarkAsComplete(); in
    Cashier: public void Handle(PaymentMessage message)
    and
    Barista : private void DeliverOrder()

    and everything started working ! as i understand now NSB doesn’t mark sagas as completed and doesn’t erase them.
    Why the code is not working with MarkAsComplete() ?
    Any thoughts ?

  6. You have to run the timeout manager (look for the additional processes that NSB provides) while executing the sample. You can start this in a separate command prompt. This is needed in order to let NSB complete the sagas and that is why removing the line with MarkAsComplete() seems to work. MarkAsComplete() internally sends a message to the timeout manager.

Comments are closed.