Getting Started
Architecture
NServiceBus
Transports
Persistence
ServiceInsight
ServicePulse
ServiceControl
Monitoring
Modernization

Routing Slips

Component: MessageRouting
NuGet Package: NServiceBus.MessageRouting (5.x)

This is a community-maintained project
Target Version: NServiceBus 8.x

Introduction

This sample demonstrates the use of the Routing Slip pattern with the MessageRouting project.

A routing slip allows a message to carry a list of destinations it should pass through. Each endpoint processes the message and then forwards it to the next stop on the slip. This enables dynamic workflows without hardcoding routes between endpoints.

Solution Overview

The solution consists of six projects:

  • Messages – Shared message definitions.
  • Sender – Initiates the message send and defines the route.
  • StepA, StepB, StepC – Processing endpoints that demonstrate how a message flows between steps.
  • ResultHost – The final destination that logs all endpoints the message passed through.

Enabling Routing Slips

All endpoints have the routing slip feature enabled:

endpointConfiguration.EnableFeature<RoutingSlips>();

Multiple Message Interpretations

Each step in the route defines its own interpretation of the message.

For example, StepA treats the message contract as follows:

public class SequentialProcess :
    ICommand
{
    public string StepAInfo { get; set; }
}

Both the Sender and ResultHost projects use the full message context by referencing the Messages project:

public class SequentialProcess :
    ICommand
{
    public string StepAInfo { get; set; }
    public string StepBInfo { get; set; }
    public string StepCInfo { get; set; }
}

When sending, all shared properties are set:

var sequentialProcess = new SequentialProcess
{
    StepAInfo = "Foo",
    StepBInfo = "Bar",
    StepCInfo = "Baz",
};

However, in each step project, handlers only work with their own specific interpretation of the message:

public class Handler :
    IHandleMessages<SequentialProcess>
{
    static ILog log = LogManager.GetLogger(typeof(Handler));

    public Task Handle(SequentialProcess message, IMessageHandlerContext context)
    {
        var routingSlip = context.Extensions.Get<RoutingSlip>();

        log.Info(message.StepAInfo);

        routingSlip.Attachments["Foo"] = "Bar";

        return Task.CompletedTask;
    }
}

Message Sending

The Sender project alternates between two send actions:

var toggle = false;
while (true)
{
    var key = Console.ReadKey();
    if (key.Key != ConsoleKey.Enter)
    {
        break;
    }
    if (toggle)
    {
        await SendToABC(endpoint);
    }
    else
    {
        await SendToAC(endpoint);
    }

    toggle = !toggle;
}

Route to A, C, and ResultHost

static Task SendToAC(IEndpointInstance endpoint)
{
    var sequentialProcess = new SequentialProcess
    {
        StepAInfo = "Foo",
        StepCInfo = "Baz",
    };

    log.Info("Sending message for step A, C");
    return endpoint.Route(sequentialProcess, Guid.NewGuid(),
        "Samples.RoutingSlips.StepA",
        "Samples.RoutingSlips.StepC",
        "Samples.RoutingSlips.ResultHost");
}

Route to A, B, C, and ResultHost

static Task SendToABC(IEndpointInstance endpoint)
{
    var sequentialProcess = new SequentialProcess
    {
        StepAInfo = "Foo",
        StepBInfo = "Bar",
        StepCInfo = "Baz",
    };

    log.Info("Sending message for step A, B, C");
    return endpoint.Route(sequentialProcess, Guid.NewGuid(),
        "Samples.RoutingSlips.StepA",
        "Samples.RoutingSlips.StepB",
        "Samples.RoutingSlips.StepC",
        "Samples.RoutingSlips.ResultHost");
}

Runtime Behavior

When routing to A, C, and ResultHost

  1. StepA receives the message
  2. StepC receives the message
  3. ResultHost receives the message
sequenceDiagram Participant Sender Participant StepA Participant StepB Participant StepC Participant ResultHost Sender ->> StepA: Route Note over StepA: Sets attachment "Foo = Bar" StepA ->> StepC: Route Note over StepC: Reads attachment "Foo" StepC ->> ResultHost: Route

When routing to A, B, C, and ResultHost

  1. StepA receives the message
  2. StepB receives the message
  3. StepC receives the message
  4. ResultHost receives the message
sequenceDiagram Participant Sender Participant StepA Participant StepB Participant StepC Participant ResultHost Sender ->> StepA: Route Note over StepA: Sets attachment "Foo = Bar" StepA ->> StepB: Route StepB ->> StepC: Route Note over StepC: Reads attachment "Foo" StepC ->> ResultHost: Route

Attachments

StepA sets a routing slip attachment:

var routingSlip = context.Extensions.Get<RoutingSlip>();

log.Info(message.StepAInfo);

routingSlip.Attachments["Foo"] = "Bar";

StepC then retrieves the attachment:

var routingSlip = context.Extensions.Get<RoutingSlip>();

log.Info(message.StepCInfo);

if (routingSlip.Attachments.TryGetValue("Foo", out var fooValue))
{
    log.Info($"Found Foo value of {fooValue}");
}