How to return data from the command bus?

I'm new to domain-based design concepts, and I had a problem returning the correct answers to the API when using the command bus with commands and command handlers for domain logic.

Suppose you created an application using a domain-centric approach. We have the back and front. The back end has all of our open API domain logic. The front end uses the API to query the application.

We build our domain logic using commands and command handlers mapped to the command bus. In our domain directory, we have a command to create a mail resource named CreatePostCommand. He mapped to the CreatePostCommandHandler handler via the command bus.

final class CreatePostCommand { private $title; private $content; public function __construct(string $title, string $content) { $this->title = $title; $this->content= $content; } public function getTitle() : string { return $this->title; } public function getContent() : string { return $this->content; } } final class CreatePostCommandHandler { private $postRepository; public function __construct(PostRepository $postRepository) { $this->postRepository = $postRepository; } public function handle(Command $command) { $post = new Post($command->getTitle(), $command->getContent()); $this->postRepository->save($post); } } 

In our API, we have an endpoint for creating a message. This is directed by the createPost method in the PostController in our application directory.

 final class PostController { private $commandBus; public function __construct(CommandBus $commandBus) { $this->commandBus = $commandBus; } public function createPost($req, $resp) { $command = new CreatePostCommand($command->getTitle(), $command->getContent()); $this->commandBus->handle($command); // How do we get the data of our newly created post to the response here? return $resp; } } 

Now, in our createPost method, we want to return the data of our newly created message in our response object so that our application on the front panel can learn about the newly created resource. This is unpleasant, since we know that by definition the command bus should not return any data. So now we are stuck in a confusing position where we do not know how to add a new message to the response object.

I'm not sure how to start this problem here, several questions come to mind:

  • Is there an elegant way to return message data in response?
  • Am I using the Command / CommandHandler / CommandBus pattern incorrectly?
  • Is this just the wrong use case for the Command / CommandHandler / CommandBus pattern?
+7
php command-pattern api-design domain-driven-design
source share
2 answers

First, note that if we connect the controller directly to the command handler, we face a similar problem:

  public function createPost($req, $resp) { $command = new CreatePostCommand($command->getTitle(), $command->getContent()); $this->createPostCommandHandler->handle($command); // How do we get the data of our newly created post to the response here? return $resp; } 

The bus introduces an indirection layer that allows you to separate the controller from the event handler, but the problem you are facing is more fundamental.

I am not sure how to do it here.

TL; DR - tell the domain which identifiers to use, and not ask the domain which identifier was used.

  public function createPost($req, $resp) { // TADA $command = new CreatePostCommand($req->getPostId() , $command->getTitle(), $command->getContent()); $this->createPostCommandHandler->handle($command); // happy path: redirect the client to the correct url $this->redirectTo($resp, $postId) } 

In short, the client, not the domain model or persistence level, is responsible for creating the identifier of the new object. The application component can read the identifier in the command itself and use it to coordinate the next state transition.

The application in this implementation simply translates the message from the DTO view to the domain view.

An alternative implementation uses the command identifier and deduces identities from this command that will be used

  $command = new CreatePostCommand( $this->createPostId($req->getMessageId()) , $command->getTitle(), $command->getContent()); 

Named UUIDs are a common choice in the latter case; they are deterministic and have low probability of collisions.

Now this answer is a bit of a hoax - we really only demonstrated that in this case we do not need the result from the command handler.

In general, we would rather have one; Post / Redirect / Get is a good idiom to use to update a domain model, but when a client receives this resource, we want to make sure that they get a version that includes the changes they just made.

If your readings and notes use the same book of notes, this is not a problem. Everything you read is always the latest version.

However, cqrs is a common architectural pattern in domain design, in which case the recording model (message processing) will be redirected to the readable model, which usually publishes outdated data. This way you can include the minimum version in the get request so that the handler knows to update its obsolete cache.

Is there an elegant way to return message data in response?

Here is an example in the sample code that you provided with your question:

 public function createPost($req, $resp) 

Think about it: $ req is an HTTP request message that is similar to your command, and $ resp is essentially a data structure descriptor into which you can write your result.

In other words, pass a callback or result descriptor with your command, and let the command handler fill in the details.

Of course, it depends on your bus supporting callbacks; not guaranteed.

Another possibility, which does not require changing the signature of your command handler, is to arrange for the controller to subscribe to events published by the command handler. You coordinate the correlation identifier between the team and the event and use this to pull the desired result event.

Specificity does not matter much - an event created during processing of a command can be written to the message bus or copied to a mailbox or ...

+8
source share

I use this approach and I return the results of the command. However, this is a solution that only works if the command handlers are part of the same process. Basically, I use a mediator, a controller, and a command handler get an instance (usually as a constructor dependency).

Pseudo code controller

 var cmd= new MyCommand(); var listener=mediator.GetListener(cmd.Id); bus.Send(cmd); //wait until we get a result or timeout var result=listener.Wait(); return result; 

Pseudo Code Handler Function

 var result= new CommandResult(); add some data here mediator.Add(result,cmd.Id); 

How do you get immediate feedback. However, this should not be used to implement a business process.

Btw, this has nothing to do with DDD, it is basically a CQS message-based approach that can be and is used in a DDD application.

+2
source share