CQRS: Command Return Values [closed]

There seems to be endless confusion about whether commands should or should not have return values. I would like to know if the confusion is simply because the participants have not stated their context or circumstances.

The Confusion

Here are examples of the confusion…

  • Udi Dahan says commands are “not returning errors to the client,” but in the same article he shows a diagram where commands indeed return errors to the client.

  • A Microsoft Press Store article states “the command…doesn’t return a response” but then goes on to give an ambiguous caution:

As battlefield experience grows around CQRS, some practices consolidate and tend to become best practices. Partly contrary to what we just stated… it is a common view today to think that both the command handler and the application need to know how the transactional operation went. Results must be known…

Well, do command handlers return values or not?

The Answer?

Taking a cue from Jimmy Bogard’s “CQRS Myths,” I think the answer(s) to this question depends on what programmatic/contextual “quadrant” you are speaking of:

+-------------+-------------------------+-----------------+
|             | Real-time, Synchronous  |  Queued, Async  |
+-------------+-------------------------+-----------------+
| Acceptance  | Exception/return-value* | <see below>     |
| Fulfillment | return-value            | n/a             |
+-------------+-------------------------+-----------------+

Acceptance (e.g. validation)

Command “Acceptance” mostly refers to validation. Presumably validation results must be given synchronously to the caller, whether or not the command “fulfillment” is synchronous or queued.

However, it seems many practitioners don’t initiate validation from within the command handler. From what I’ve seen, it is either because (1) they’ve already found a fantastic way to handle validation at the application layer (i.e. an ASP.NET MVC controller checking valid state via data annotations) or (2) an architecture is in place which assumes commands are submitted to an (out of process) bus or queue. These latter forms of asynchrony generally don’t offer synchronous validation semantics or interfaces.

In short, many designers might want the command handler to provide validation results as a (synchronous) return value, but they must live with the restrictions of they asynchrony tools they are using.

Fulfillment

Regarding “fulfillment” of a command, the client who issued the command might need to know the scope_identity for a newly created record or perhaps failure information – such as “account overdrawn.”

In a real-time setting it seems that a return value makes the most sense; exceptions should not be used to communicate business-related failure outcomes. However, in a “queuing” context…return values naturally make no sense.

This is where all the confusion can perhaps be summarized:

Many (most?) CQRS practitioners assume they will now, or in the future, incorporate an asynchrony framework or platform (a bus or queue) and thus proclaim that command handlers do not have return values. However, some practitioners have no intention of using such event-driven constructs, and so they will endorse command handlers that (synchronously) return values.

So, for example, I believe a synchronous (request-response) context was assumed when Jimmy Bogard provided this sample command interface:

public interface ICommand<out TResult> { }

public interface ICommandHandler<in TCommand, out TResult>
    where TCommand : ICommand<TResult>
{
    TResult Handle(TCommand command);
}

His Mediatr product is, after all, an in-memory tool. Given all this, I think the reason Jimmy carefully took the time to produce a void return from a command was not because “command handlers should not have return values,” but instead because he simply wanted his Mediator class to have a consistent interface:

public interface IMediator
{
    TResponse Request<TResponse>(IQuery<TResponse> query);
    TResult Send<TResult>(ICommand<TResult> query);  //This is the signature in question.
}

…even though not all commands have a meaningful value to return.

Repeat and Wrap Up

Am I correctly capturing why there is confusion on this topic? Is there something I’m missing?

Update (6/2020)

With the help of the answers given, I think I’ve untangled the confusion. Put simply, if a CQRS command is capable of returning a success/failure indicating completion status, then a return value makes sense. This includes returning a new DB row identity, or any result that does not read or return domain model (business) content.

I think where “CQRS command” confusion emerges, is over the definition and role of “asynchrony”. There is a big difference between “task based” async IO, and an asynchronous architecture (e.g. queue based middle-ware). In the former, the async “task” can and will provide the completion result for the async command. However, a command sent to RabbitMQ will not similarly receive a request/reponse completion notification. It is this latter context of async-architecture that causes some to say “there is no such thing as an async command” or “commands do not return values.”

Answer

Following the advice in Tackling Complexity in CQRS by Vladik Khononov suggests command handling can return information relating to its outcome.

Without violating any [CQRS] principles, a command can safely return the following data:

  • Execution result: success or failure;
  • Error messages or validation errors, in case of a failure;
  • The aggregate’s new version number, in case of success;

This information will dramatically improve the user experience of your system, because:

  • You don’t have to poll an external source for command execution result, you have it right away. It becomes trivial to validate commands, and to return error messages.
  • If you want to refresh the displayed data, you can use the aggregate’s new version to determine whether the view model reflects the executed command or not. No more displaying stale data.

Daniel Whittaker advocates returning a “common result” object from a command handler containing this information.

Attribution
Source : Link , Question Author : Brent Arias , Answer Author : Community

Leave a Comment