NServiceBus 5.0 behaviors in action: routing slips

I’ve wrote in the past how routing slips can provide a nice alternative to NServiceBus sagas, using a stateless, upfront approach. In NServiceBus 4.x, it was quite clunky to actually implement them. I had to plug in to two interfaces that didn’t really apply to routing slips, only because those were the important points in the pipeline to get the correct behavior.

In NServiceBus 5, these behaviors are much easier to build, because of the new behavior pipeline features. Behaviors in NServiceBus are similar to HttpHandlers, or koa.js callbacks, in which form a series of nested wrappers around inner behaviors in a sort of Russian doll model. It’s an extremely popular model, and most modern web frameworks include some form of it (Web API filters, node, Fubu MVC behaviors, etc.)

Behaviors in NServiceBus are applied to two distinct contexts: incoming messages and outgoing messages. Contexts are represented by context objects, allowing you to get access to information about the current context instead of doing things like dependency injection to do so.

In converting the route supervisor in my routing slips implementation, I greatly simplified the whole thing, and got rid of quite a bit of cruft.

Creating the behavior

To first create my behavior, I need to create an implementation of an IBehavior interface with the context I’m interested in:

public class RouteSupervisor
    : IBehavior<IncomingContext> {
    
    public void Invoke(IncomingContext context, Action next) {
        next();
    }
}

Next, I need to fill in the behavior of my invocation. I need to detect if the current request has a routing slip, and if so, perform the operation of routing to the next step. I’ve already built a component to manage this logic, so I just need to add it as a dependency:

private readonly IRouter _router;

public RouteSupervisor(IRouter router)
{
    _router = router;
}

Then in my Invoke call:

public void Invoke(IncomingContext context, Action next)
{
    string routingSlipJson;

    if (context.IncomingLogicalMessage.Headers.TryGetValue(Router.RoutingSlipHeaderKey, out routingSlipJson))
    {
        var routingSlip = JsonConvert.DeserializeObject<RoutingSlip>(routingSlipJson);

        context.Set(routingSlip);

        next();

        _router.SendToNextStep(routingSlip);
    }
    else
    {
        next();
    }
}

I first pull out the routing slip from the headers. But this time, I can just use the context to do so, NServiceBus manages everything related to the context of handling a message in that object.

If I don’t find the header for the routing slip, I can just call the next behavior. Otherwise, I deserialize the routing slip from JSON, and set this value in the context. I do this so that a handler can access the routing slip and attach additional contextual values.

Next, I call the next action (next()), and finally, I send the current message to the next step.

With my behavior created, I now need to register my step.

Registering the new behavior

Since I have now a pipeline of behavior, I need to tell NServiceBus when to invoke my behavior. I do so by first creating a class that represents the information on how to register this step:

public class Registration : RegisterStep
{
    public Registration()
        : base(
            "RoutingSlipBehavior", typeof (RouteSupervisor),
            "Unpacks routing slip and forwards message to next destination")
    {
        InsertBefore(WellKnownStep.LoadHandlers);
    }
}

I tell NServiceBus to insert this step before a well-known step, of loading handlers. I (actually Andreas) picked this point in the pipeline because in doing so, I can modify the services injected into my step. This last piece is configuring and turning on my behavior:

public static BusConfiguration RoutingSlips(this BusConfiguration configure)
{
    configure.RegisterComponents(cfg =>
    {
        cfg.ConfigureComponent<Router>(DependencyLifecycle.SingleInstance);
        cfg.ConfigureComponent(b => 
            b.Build<PipelineExecutor>()
                .CurrentContext
                .Get<RoutingSlip>(),
           DependencyLifecycle.InstancePerCall);
    });
    configure.Pipeline.Register<RouteSupervisor.Registration>();

    return configure;
}

I register the Router component, and next the current routing slip. The routing slip instance is pulled from the current context’s routing slip – what I inserted into the context in the previous step.

Finally, I register the route supervisor into the pipeline. With the current routing slip registered as a component, I can allow handlers to access the routing slip and add attachment for subsequent steps:

public RoutingSlip RoutingSlip { get; set; }

public void Handle(SequentialProcess message)
{
    // Do other work

    RoutingSlip.Attachments["Foo"] = "Bar";
}

With the new pipeline behaviors in place, I was able to remove quite a few hacks to get routing slips to work. Building and registering this new behavior was simple and straightforward, a testament to the design benefits of a behavior pipeline.

About Jimmy Bogard

I'm a technical architect with Headspring in Austin, TX. I focus on DDD, distributed systems, and any other acronym-centric design/architecture/methodology. I created AutoMapper and am a co-author of the ASP.NET MVC in Action books.
This entry was posted in NServiceBus. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Excellent post Jimmy, always nice to see a real world use case! For others reading this please also take a look at our doco, yes we’re getting our act together regarding doco : ), and lets us know what’s missing!

    http://docs.particular.net/nservicebus/nservicebus-pipeline-intro

    Also stay tuned for the recording of Jimmy’s talk on Routing Slip’s on this years NSBCON in New York

  • Raham Rahil

    Is always a pleasure to read your articles Jimmy, but it is me or the Github code view is getting harder and harder to read?

    • Raham Rahil

      Nevermind the code is fine now, it might be a problem with Firefox

  • Pingback: The Morning Brew - Chris Alcock » The Morning Brew #1708()

  • rob

    Hi Jimmy,
    Enjoyed reading the article. Question about the implementation and the decision to put the command to forward the message to the next stepinto the RouteSupervisor and Router. Most samples I see have instructions to send command or publish event messages made explicitly say in a message handler. Is this to make it convenient for developers to implement the routing slip in their projects or is there a deeper design decision at play here. Is it because a routing slip handler should not know it is part of a sequence of steps? The sample provided leaks knowledge that there is more going on than just a handler’s particular step as to access handler specific information from the message, there is a custom command message which has a property accessor for the step info.
    Does this make sense?
    Thanks

    • jbogard

      Yep, that’s it. Handlers are unaware they’re part of a sequence, so there’s an external force moving the routing slip to the next step. There might be information that needs to be “filled in”, via attachments, but otherwise handlers are focusing on handling. This is more in line with the original pattern described in the Enterprise Integration Patterns book.