Sending Messages to the Original Sender with Wolverine

Sending Messages to the Original Sender with Wolverine

Yesterday I blogged about a small, convenience feature we snuck into he release of?Wolverine 3.0?last week for a?JasperFx Software?customer I wrote about in Combo HTTP Endpoint and Message Handler with Wolverine?3.0.?Today I’d like to show some additions to Wolverine 3.0 just to improve its ability to send responses back to the original sending application or raise other messages in response to problems.

One of Wolverine’s main functions is to be an asynchronous messaging framework where we expect messages to come into our Wolverine systems through messaging brokers like Azure Service Bus or Rabbit MQ or AWS SQS from another system (or you can message to yourself too of course). A frequent question from users is what if there’s a message that can’t be processed for some reason and there’s a need to send a message back to the originating system or to create some kind of alert message to a support person to intervene?

Let’s start with the assumption that at least some problems can be found with validation rules early in message processing such that you can determine early that a message is not able to be processed — and if this happens, send a message back to the original sender telling it (or a person) so. In the Wolverine documentation, we have this middleware for looking up account information for any message that implements an IAccountCommand interface:

// This is *a* way to build middleware in Wolverine by basically just
// writing functions/methods. There's a naming convention that
// looks for Before/BeforeAsync or After/AfterAsync
public static class AccountLookupMiddleware
{
    // The message *has* to be first in the parameter list
    // Before or BeforeAsync tells Wolverine this method should be called before the actual action
    public static async Task<(HandlerContinuation, Account?)> LoadAsync(
        IAccountCommand command,
        ILogger logger,
 
        // This app is using Marten for persistence
        IDocumentSession session,
 
        CancellationToken cancellation)
    {
        var account = await session.LoadAsync<Account>(command.AccountId, cancellation);
        if (account == null)
        {
            logger.LogInformation("Unable to find an account for {AccountId}, aborting the requested operation", command.AccountId);
        }
 
        return (account == null ? HandlerContinuation.Stop : HandlerContinuation.Continue, account);
    }
}        

Now, let’s change the middleware up above to send a notification message back to whatever the original sender is if the referenced account cannot be found. For the first attempt, let’s do it by directly injecting IMessageContext (IMessageBus, but with some specific API additions we need in this case) from Wolverine like so:

public static class AccountLookupMiddleware
{
    // The message *has* to be first in the parameter list
    // Before or BeforeAsync tells Wolverine this method should be called before the actual action
    public static async Task<(HandlerContinuation, Account?)> LoadAsync(
        IAccountCommand command,
        ILogger logger,
 
        // This app is using Marten for persistence
        IDocumentSession session,
         
        IMessageContext bus,
 
        CancellationToken cancellation)
    {
        var account = await session.LoadAsync<Account>(command.AccountId, cancellation);
        if (account == null)
        {
            logger.LogInformation("Unable to find an account for {AccountId}, aborting the requested operation", command.AccountId);
 
            // Send a message back to the original sender, whatever that happens to be
            await bus.RespondToSenderAsync(new InvalidAccount(command.AccountId));
 
            return (HandlerContinuation.Stop, null);
        }
 
        return (HandlerContinuation.Continue, account);
    }
}        

Okay, hopefully not that bad. Now though, let’s utilize Wolverine’s OutgoingMessages type to relay that message with this functionally equivalent code:

public static class AccountLookupMiddleware
{
    // The message *has* to be first in the parameter list
    // Before or BeforeAsync tells Wolverine this method should be called before the actual action
    public static async Task<(HandlerContinuation, Account?, OutgoingMessages)> LoadAsync(
        IAccountCommand command,
        ILogger logger,
 
        // This app is using Marten for persistence
        IDocumentSession session,
 
        CancellationToken cancellation)
    {
        var messages = new OutgoingMessages();
        var account = await session.LoadAsync<Account>(command.AccountId, cancellation);
        if (account == null)
        {
            logger.LogInformation("Unable to find an account for {AccountId}, aborting the requested operation", command.AccountId);
 
            messages.RespondToSender(new InvalidAccount(command.AccountId));
            return (HandlerContinuation.Stop, null, messages);
        }
 
        // messages would be empty here
        return (HandlerContinuation.Continue, account, messages);
    }
}        

As of Wolverine 3.0, you’re now able to send messages from “before / validate” middleware by either using IMessageBus/IMessageContext or OutgoingMessages. This is in addition to the older functionality to possibly send messages on certain message failures, as shown below in a sample from the Wolverine documentation on custom error handling policies:

theReceiver = await Host.CreateDefaultBuilder()
    .UseWolverine(opts =>
    {
        opts.ListenAtPort(receiverPort);
        opts.ServiceName = "Receiver";
 
        opts.Policies.OnException<ShippingFailedException>()
            .Discard().And(async (_, context, _) =>
            {
                if (context.Envelope?.Message is ShipOrder cmd)
                {
                    await context.RespondToSenderAsync(new ShippingFailed(cmd.OrderId));
                }
            });
    }).StartAsync();        

You’ve got options! Wolverine does have a concept of “respond to sender” if you’re sending messages between Wolverine applications that will let you easily send a new message inside a message handler or message handler exception handling policy back to the original sender. This functionality also works, admittedly in a limited capacity, with interoperability between MassTransit and Wolverine through Rabbit MQ.

要查看或添加评论,请登录

Jeremy Miller的更多文章

  • Nobody Codes a Bad System On Purpose

    Nobody Codes a Bad System On Purpose

    I have been writing up a little one pager for a JasperFx Software client for their new CTO on why and how their…

  • New Critter Stack Features

    New Critter Stack Features

    JasperFx Software offers custom consulting engagements or ongoing support contracts for any part of the Critter Stack…

  • We Don’t Need No Stinkin’ Repositories and Other Observations on DotNetRocks

    We Don’t Need No Stinkin’ Repositories and Other Observations on DotNetRocks

    I had a conversation with the DotNetRocks fellows a little while back that posted today, ostensibly about the “Vertical…

    5 条评论
  • Critter Stack Roadmap Update

    Critter Stack Roadmap Update

    The Critter Stack Core decided last week that it was time to get going on the next round of releases for what will be…

  • Retry on Errors in Wolverine

    Retry on Errors in Wolverine

    Coaching my daughter’s 1st/2nd grade basketball team is a trip. I don’t know that the girls are necessarily learning…

    1 条评论
  • Wolverine for MediatR Users

    Wolverine for MediatR Users

    I happened to see this post from Milan Jovanovi? today about a little backlash to the MediatR library. For my part, I…

  • Kicking off a new YouTube Channel on the Critter Stack

    Kicking off a new YouTube Channel on the Critter Stack

    Jeffry Gonzalez and I have kicked off what we plan to be a steady stream of content on the "Critter Stack" (Marten…

    2 条评论
  • Wringing More Scalability out of Event Sourcing with the Critter Stack

    Wringing More Scalability out of Event Sourcing with the Critter Stack

    JasperFx Software works with our customers to help wring the absolute best results out of our customer’s usage of the…

  • Why the Critter Stack is Good

    Why the Critter Stack is Good

    JasperFx Software already has a strong track record in our short life of helping our customers be more successful using…

    4 条评论
  • What would be helpful to know about building "Modular Monoliths?"

    What would be helpful to know about building "Modular Monoliths?"

    Suddenly every body and their little brother has been asking questions about this, that, or the other messaging pattern…

    2 条评论

社区洞察

其他会员也浏览了