Getting Started
Architecture
NServiceBus
Transports
Persistence
ServiceInsight
ServicePulse
ServiceControl
Monitoring

Routing Slips

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

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

Introduction

Uses the Routing Slip pattern feature of the MessageRouting project.

Code walk-through

The solution consists of 6 Projects

  • Messages: The shared message definitions.
  • Sender: Initiates the message sends.
  • StepA, StepB, StepC: The handling endpoints to to show how the message flows between endpoints.
  • ResultHost: The final destination endpoint for messages that logs all the endpoints the message was routed through.

Enabling Routing Slips Feature

All endpoints have routing slips enabled:

endpointConfiguration.EnableFeature<RoutingSlips>();

Multiple message interpretations

Each step in the routing has its own definition of the message. For example StepA considers the message contract to be.

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

Both the Sender and the ResultHost have 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; }
}

On Send all share properties are set.

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

But in each step project they are only aware of the specific their specific message interpretations:

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 message
  2. StepC receives message
  3. ResultHost receives 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: Read Attachment "Foo" StepC->> ResultHost: Route

When routing to A, B, C and ResultHost

  1. StepA receives message
  2. StepB receives message
  3. StepC receives message
  4. ResultHost receives 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: Read Attachment "Foo" StepC->> ResultHost: Route

Attachments

Note that StepA sets a routing slip attachment:

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

log.Info(message.StepAInfo);

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

Which is then retrieved by StepC

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}");
}