cqrs: where to perform an "action" of a command that calles an external resource?

Hello,

Where should we perform the “action” of a command, that calls to an external resource?

Example

We have an aggregate Document. We need to send it, therefore, we have an SendDocumentCommand, and a resulting DocumentSendEvent.
Sending documents can go wrong. If it goes wrong, we do want the event DocumentSendEvent, as the document was not send.

How to implement this with CQRS?

Solution 1:
First, send the document to the external resource. If this succeeds, apply the command “SendDocument” on the command gateway, hence on the Document aggregate.

try { externalResource.send(content); } catch (Exception exc) { throw new SendDocumentFailed(exc); } commandGateway.sendAndWait(SendDocumentCommand.create(documentId);

The code in Document creates the DocumentSentEvent from the command.

@CommandHandler public void handle(SendDocumentCommand command) { validate(command); apply(new DocumentSentEvent(command.getDocumentId())); }

I do not like this: the name SendDocument would not be correct, it would be more like “RegisterThatDocumentHasBeenSent”. But the business process is really “Send Document”.
Also, if Document needs to perform additional validations on the command, it would be too late: the document is already sent.
Or certain validations should be done before sending the document, but then, you move business logic outside the Aggregate.

Solution 2:
The aggregate handles the command, and sends the document. If sending the document succeeds, apply the DocumentSentEvent, otherwise, raise exeption.

SendDocumentCommand is part of the domain. A SendDocumentCommand has an action object implementation, passed in as a lambda in its factory method. (The Document aggregate sees only an “Action” interface with a “perform()” method).

So the Document code would be:

`
SendDocumentCommand command = SendDocumentCommand.create(documentId, () -> externalResource.send(content)); //action passed as a lambda i.e.
commandGateway.sendAndWait(command);

`

Document aggregate

@CommandHandler public void handle(SendDocumentCommand command) { validate(command); try { command.getAction().perform(); apply(new DocumentSentEvent(command.getDocumentId())); } catch(Exception exc) { throw new SendDocumentFailed(exc); } }

Wich solution is commonly used in CQRS implementations? Is it common to put some action inside a command? If this refers to a resource, it would make the command unserializable. But is that bad?

My take: a command changes the state of the aggregate, and any actions that need to happen in consequence should go in the EventHandler (not EventSourcingHandler careful). Sending state (ie the document bytes or whatever) to an external system is an example of such consequence, much like broadcasting an event to some topic. How you do this is a matter of taste. Your DocumentSent event could contain some id that the eventsourcinghandler can use to get the payload and send it on. Or you broadcast an event to a topic and downstream systems read the content back, various options.

HTH
Jorg

Thanks Jorg

So, the system would send the document (action) as a result of the domain event, DocumentSend. Makes sence to me that you do interactions with other systems as the result what you have decided in the domain.
But what if sending the document fails? You have spanwed a DocumentSendEvent in the domain, which is inconsistent. How do you make the Document (eventually) consistent with what really happened again? Issue a new command? Who would issue that command?

Stijn

Oops first a correction in my previous reply:

Your DocumentSent event could contain some id that the EVENTHANDLER can use to get the payload and send it on.

Also as a sidenote, event names always indicate an action that completed so they should be in the past tense (ie DocumentSent), this makes it easier to reason about them.

Now about the consistency, things get philosphical real fast. If you really care that the other system is in sync at all times, question your bounded context maybe they should not be separate systems. Otherwise it depends, you could have a service that first handles the sending of the document (retries, error handling etc), and when this is ok and done you send a MarkDocumentAsSent command to the AR which would then set the state and emit the DocumentSent event. A saga pattern could be used as well i think. You would send a SendDocument(id, bytes) command to the other system, wait for the DocumentReceived(id) event, then send MarkDocumentAsSent and complete the saga upon DocumentSent.

Overall a very technical business case, not the most obvious example to demonstrate the usefulness of AR and CQRS.

HTH
Jorg