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?