115

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."

Brent Arias
  • 29,277
  • 40
  • 133
  • 234
  • Nice question but not quite clear. Commands are just VO, not methods, they do not return anything; probably you are refering to handling of commands; please specify the level/layer: presentation (i.e. rest), application, business level (aggregates). – Constantin Galbenu Apr 16 '17 at 04:05
  • 1
    Recommended reading: https://martinfowler.com/bliki/CommandQuerySeparation.html http://udidahan.com/2010/08/31/race-conditions-dont-exist/ https://tools.ietf.org/html/rfc7231#page-25 (4.3.3-4.3.5) – VoiceOfUnreason Apr 16 '17 at 21:45

4 Answers4

42

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.

Community
  • 1
  • 1
Ben Smith
  • 2,369
  • 20
  • 17
  • 4
    What about command that creates new object, should it not return at least the id of the created object? – Greyshack Jul 16 '19 at 09:17
  • 9
    @Greyshack This is presumably why most ES/CQRS resources suggest using GUIDs for aggregate/entity IDs -- the client can generate the ID and include it in the creation command, instead of leaving it for the backend to generate an ID. – Sam Jones Oct 16 '19 at 16:46
  • The controller could also create the ID so the command handler itself doesn't need to return anything (but not to say that the command handler should never return anything) – Va5ili5 May 11 '23 at 19:01
12

Well, do command handlers return values or not?

They should not return Business Data, only meta data (regarding the success or failure of executing the command). CQRS is CQS taken to a higher level. Even if you would break the purist's rules and return something, what would you return? In CQRS the command handler is a method of an application service that loads the aggregate then calls a method on the aggregate then it persists the aggregate. The intend of the command handler is to modify the aggregate. You wouldn't know what to return that would be independent of the caller. Every command handler caller/client would want to know something else about the new state.

If the command execution is blocking (aka synchronous) then all you would need to know if whether the command executed successfully or not. Then, in a higher layer, you would query the exact thing that you need to know about the new application's state using a query-model that is best fitted to your needs.

Think otherwise, if you return something from a command handler you give it two responsibilities: 1. modify the aggregate state and 2. query some read-model.

Regarding command validation, there are at least two types of command validation:

  1. command sanity check, that verifies that a command has the correct data (i.e. an email address is valid); this is done before the command reaches the aggregate, in the command handler (the application service) or in the command constructor;
  2. domain invariants check, that is performed inside the aggregate, after the command reaches the aggregate (after a method is called on the aggregate) and it checks that the aggregate can mutate to the new state.

However, if we go some level up, in the Presentation layer (i.e. a REST endpoint), the client of the Application layer, we could return anything and we wont' break the rules because the endpoints are designed after the use cases, you know exactly what you want to return after a command is executed, in every use case.

Constantin Galbenu
  • 16,951
  • 3
  • 38
  • 54
  • 4
    _"you know exactly what you want to return after a command is executed, in every use case"_ - no, I don't. If client requests service A, which has CQRS and inside command handler remaps some data and calls service B in the background (on behalf of a client), which then returns ID of newly create resource, how would I return this ID to the client if service A's command does not return value? – Wirone May 07 '18 at 13:47
  • 4
    @Wirone In general, in CQRS are used GUIDs, so you know the ID of the entity *even before* you execute the command. So, you know the ID and you know the use-case => you know the Readmodel – Constantin Galbenu May 07 '18 at 14:12
  • 3
    "They should not. CQRS is CQS taken to a higher level." CQRS is _higher_ level idea and it doesn't care about methods. The main idea in CQRS is to avoid mixing write and read models, but command handler can (MUST) return errors and even produced events. – Misanthrope Jul 29 '18 at 06:59
  • 2
    @Misanthrope I agree, please read again my answer, especially the last paragraph.However, the command handler (just to be sure: the method that loads the Aggregate, calls the command method and then persist the Aggregate) should not return anything. If you need events then subscribe to them and the HTTP handler (not the Command handler!) can collect those events and return them. This applies to the exceptions also. Hell, the HTTP handler (or whatever you have above the Command handler) can query a readmodel or something and return some state. – Constantin Galbenu Jul 30 '18 at 06:10
  • 2
    "If you need events then subscribe to them" I see no reason to do it in this case. Command handler, generally speaking, just converts command into events. Getting events in this case is just more explicit way. – Misanthrope Jul 30 '18 at 16:06
  • 1
    @Misanthrope And what exactly do you do with those events? – Constantin Galbenu Jul 30 '18 at 16:08
  • 1
    https://stackoverflow.com/questions/43433318/cqrs-command-return-values#51601998 – Misanthrope Jul 30 '18 at 20:43
7

CQRS and CQS are like microservices and class decomposition: the main idea is the same ("tend to small cohesive modules"), but they lie on different semantic levels.

The point of CQRS is to make write/read models separation; such low-level details like return value from specific method is completely irrelevant.

Take notice on the following Fowler's quote:

The change that CQRS introduces is to split that conceptual model into separate models for update and display, which it refers to as Command and Query respectively following the vocabulary of CommandQuerySeparation.

It is about models, not methods.

Command handler may return anything excepting read models: status (success/failure), generated events (it's primary goal of command handlers, btw: to generate events for the given command), errors. Command handlers very often throw unchecked exception, it is example of output signals from command handlers.

Moreover, author of the term, Greg Young, says that commands are always sync (otherwise, it becomes event): https://groups.google.com/forum/#!topic/dddcqrs/xhJHVxDx2pM

Greg Young

actually I said that an asynchronous command doesn't exist :) its actually another event.

Community
  • 1
  • 1
Misanthrope
  • 454
  • 4
  • 12
3

Reply for @Constantin Galbenu, I faced limit.

@Misanthrope And what exactly do you do with those events?

@Constantin Galbenu, in most cases I don't need them as result of the command, of course. In some cases -- I need to notify client in response of this API request.

It's extremely useful when:

  1. You need to notify about error via events instead of exceptions. It happens usually when your model need to be saved (for example, it counts number of attempts with wrong code/password) even if error happened. Also, some guys doesn't use exceptions for business errors at all -- only events (http://andrzejonsoftware.blogspot.com/2014/06/custom-exceptions-or-domain-events.html) There is no particular reason to think that throwing business exceptions from command handler is OK, but returning domain events is not.
  2. When event happens only with some circumstances inside of your aggregate root.

And I can provide example for the second case. Imagine we make Tinder-like service, we have LikeStranger command. This command MAY result in StrangersWereMatched if we like person who already liked us before. We need to notify mobile client in response whether match was happened or not. If you just want to check matchQueryService after the command, you may find match there, but there is no gurantee that match was happened right now, because SOMETIMES Tinder shows already matched strangers (probably, in unpopulated areas, maybe inconsistency, probably you just have 2nd device, etc.).

Checking response if StrangersWereMatched really happened right now is so straightforward:

$events = $this->commandBus->handle(new LikeStranger(...));

if ($events->contains(StrangersWereMatched::class)) {
  return LikeApiResponse::matched();
} else {
  return LikeApiResponse::unknown();
}

Yes, you can introduce command id, for example, and make Match read model to keep it:

// ...

$commandId = CommandId::generate();

$events = $this->commandBus->handle(
  $commandId,
  new LikeStranger($strangerWhoLikesId, $strangerId)
);

$match = $this->matchQueryService->find($strangerWhoLikesId, $strangerId);

if ($match->isResultOfCommand($commandId)) {
  return LikeApiResponse::matched();
} else {
  return LikeApiResponse::unknown();
}

... but think about it: why do you think that first example with straightforward logic is worse? It doesn't violate CQRS anyway, I just made the implicit explicit. It is stateless immutable approach. Less chances to hit a bug (e.g. if matchQueryService is cached/delayed [not instantly consistent], you have a problem).

Yes, when the fact of matching is not enough and you need to get data for response, you have to use query service. But nothing prevents you to receive events from command handler.

Misanthrope
  • 454
  • 4
  • 12