Search code examples
phprabbitmqcqrsreactphp

How to retrieve newly created entity by asynchronous command (CQRS) in same php process?


I am implementing PHP application with CQRS.

Let's say I have CreateOrderCommand and when I do

$command = new CreateOrderCommand(/** some data**/);
$this->commandBus->handle($command);

CommandBus now just pass command to proper CreateOrderCommandHandler class as easily as:

abstract class SimpleCommandBus implements CommandBus
{
    /** @var ICommandHandlerLocator */
    protected $locator;

    /**
     * Executes command
     *
     * @param Command $command
     */
    public function handle(Command $command)
    {
        $handler = $this->locator->getCommandHandler($command);
        $handler->handle($command);
    }
}

Everything ok.

But handling is void method, so I do not know anything about progress or result. What can I do to be able to for example fire CreateOrderCommand and then in same process acquire newly created entity id (probably with some passive waiting for its creation)?

public function createNewOrder(/** some data**/){
  $command = new CreateOrderCommand(/** some data**/);
  $this->commandBus->handle($command);
  // something that will wait until command is done
  $createdOrder = // some magic that retrieves some adress to result data
  return $createdOrder;
}

And to get closer of what CQRS can provide, command bus should be able to have RabbitMqCommandBus implementation that just serializes command and sends it to rabbit queue.

So, then the process that finally handles command might be some running consumer and some kind of communication between processes is needed here - to be able to somehow inform original user process from consumer, that it is done (with some information, for example id of new entity).

I know that there is solution with GUID - I could mark command with GUID. But then what:

public function createNewOrder(/** some data**/){
  $command = new CreateOrderCommand(/** some data**/);
  $this->commandBus->handle($command);
  $guid = $command->getGuid();
  // SOME IMPLEMENTATION
  return $createdOrder;
}

SOME IMPLEMENTATION should do some checking of events (so I need to implement some event system too) on command with specific GUID, to be able to for example echo progress or on OrderCreatedEvent just return it's ID that I would get from that event. Consumer process that asynchronously handles command might for example feed events to rabbit and user client would taking them and do proper response (echo progress, return newly created entity for example).

But how to do that? And is solution with GUID the only one? What are acceptable implementations of solutions? Or, what point am I missing? :)


Solution

  • The easiest solution to get information about id of created aggregate/entity is to add it to the command. So the frontend generates the id and pass it with the data. But to make this solution works, you need to make use of uuid instead of normal database integers, otherwise you may find yourself duplicating identifiers on the db side.
    If the command is async and perform so time consuming actions, you can for sure publish events from the consumer. So the client via.e.g. websockets receives the informations in real time. Or ask the backend about existance of the order with the id from the command, from time to time and when the resource exists, redirect him to the right page.