3

In a CQRS/async microservices context, I have a command handler e.g. SendOrderConfirmationEmailCommandHandler implemented in a communications service.

This depends on information from the orders service, which can be retrieved via an GetOrderDetailsQueryHandler.

The sequence of messages for this process is:

SendOrderConfirmationEmailCommand -> GetOrderDetailsQuery -> OrderDetailsRetrievedEvent

Since all messages (including the GetOrderDetailsQuery) are asynchronous by necessity the process executing the SendOrderEmailCommandHandler would exit after dispatching an GetOrderDetailsQuery and then would somehow need to resume where it left off once the OrderDetailsRetrievedEvent is handled.

What sort of pattern can be implemented to deal with bridging the gap between firing off a query and the response message being received such that we are able to retain and link back to the original command message instance for which we have retrieved order details?

Can or should this be handled in the CommandHandler or elsewhere instead?

3

You are most likely to see analogous examples if you search for "process manager" or "saga".

Speaking broadly, what you are describing is a protocol, with at least three participants; the command source, the order details database, and the email service itself.

The responsibility of the protocol is to keep track; it is doing the book keeping, which is to say it is recording the events that are part of this interaction. Here, the events would be

CommandReceived
OrderDetailsRetrieved
EmailSent

(Choose better spellings if you can). When one of these things happens, the process manager writes it down. When it is the process manager's turn to act, the manager looks at the events it has seen, computes what actions (if any) should be taken next, and reports/executes those actions.

Report vs execution is a matter of style

List<Action> actions = processManager.actions()

Here, the process manager gives you an in memory representation of the actions, and the consumer can decide how to deal with them.

processManager.act(orderDetailsClient, emailClient)

Here, the process manager computes the action to take, and invokes the appropriate method on the appropriate client.

Cory Benfield's talk Building Protocol Libraries the Right Way is a good starting point.

  • Thanks for your response, I think with the saga or process manager patterns my problem still remains in that I'm unsure of what sort of mechanism to use to essentially 'resume' a process asynchronously. The order details are stored in a different service to the communications service (which is responsible for sending the emails) and so when we view the execution of the process to send the order confirmation email - that thread will need to halt execution when it dispatches the async request to retrieve order details and then resume where it left off once those details have been retrieved. – yogibear Mar 11 '18 at 22:19
  • It seems I would still need to solve that same problem when it comes to sagas, the saga may dispatch some action to ultimately sent the email once the order details have been retrieved, but it would need to track and correlate the original message relating to the order detail query in order to determine the reason for querying the order to begin with. Would that generally be managed by some in memory queue of prior events or commands that are part of a 'live' process? – yogibear Mar 11 '18 at 22:26
1

You need to define a business process for sending the email. The process is like an entity, it has an ID and some state that "remembers" in which phase it is. The process is persisted on a durable persistence and it is loaded/saved by its ID.

Now comes the important part: you must include the process ID as a correlation identifier in all asynchronous operations that it initiates and must be present also in all the replies. In this way, when the asynchronous reply is available you use the correlation ID to identify the process, load it and bases on its current state you take the next action or stop it if it is done.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.