Dealing with queued command handler response. CQRS
Asked Answered
O

3

6

Consider the situation:

public class OrderController {
    IBus bus;

    public OrderController(IBus bus) {
        this.bus = bus;
    }

    public ActionResult Checkout(String orderId) {
        var command = new CheckoutOrderCommand(orderId);
        bus.Send(command);

        return Redirect("on-success-url");
    }
}

The corresponding command handler (defined in a separate assembly) is awaiting for the incomming message to be processed.

But we have already said that everything is ok by sending return Redirect("on-success-url");

  1. What if handler fails to save changes to the domain?

    Well, it is possible to use a queue on command handler side to publish response, to subscribe web application to the queue.

  2. How does the end user get immediate/synchronous ui response, which would reflect the real changes made to the domain? Should I do so?

  3. Is handling incomming commands by means of message bus is only good for a background tasks without acknowledgment?

Ossuary answered 13/3, 2015 at 7:41 Comment(0)
F
8

This is taken from «Busting some CQRS myths», Jimmy Bogard blog that might be of interest to you:

Myth #4 – Commands are fire-and-forget

How many business processes in your line of work are truly fire and forget? You typically need at least a synchronous acknowledgement that the command was received and accepted. If you’re doing an async send of the command, how do you notify the user? Email? Are they OK with this?

Instead, with commands that need heavy lifting to fulfill the request, we really have two things going on:

  • Accepting the request
  • Fulfilling the request

Accepting the request should be synchronous. Fulfilling need not be. But for the model of an asynchronous fulfillment, we still will likely need ways of correlating requests etc., and this is where you often see workflows/processes/sagas come into play.

I always try to make my commands synchronous. If there is a long process time, I'm using something like process manager or saga.

Fanchet answered 13/3, 2015 at 11:48 Comment(0)
A
4

If you indeed want to send a request and wait on the response, you can use the IRequestClient interface, and add an instance of the MessageRequestClient to your container - and use that dependency instead of using IBus directly. This will make it so that you can await on the response from the request, and return something on the API.

Of course, you've coupled your controller to the availability of the service - and that's not always a bad thing - but something to be aware of.

Example code of using the request client can be found in the documentation, which is a web-case exactly like you describe.

http://docs.masstransit-project.com/en/mt3/usage/request_response.html

Auctioneer answered 13/3, 2015 at 16:3 Comment(0)
S
2

This somewhat depends on your command bus implementation. Some systems allow command handlers to send replies back to the original sender.

The standard solution in your case is to not acknowledge the actual checkout, but rather to just acknowledge the receipt of the command and the checkout is now being processed. For checkouts this usually is totally fine because orders take a couple of hours to being processed anyways.

If you really need to show some immediate results, you can poll another service for the state of completion and/or timeout when there is no expected result within a given time-frame.

If this is too complex for you, don't use an asynchronous command bus, but rather do synchronous command handling where failures are immediately propagated to the client.

Smithsonite answered 13/3, 2015 at 8:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.