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
- StepA receives message
- StepC receives message
- 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
- StepA receives message
- StepB receives message
- StepC receives message
- 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}");
}