A simple helper to implement CommandBus and QueryBus

v1.0 2018-12-18 16:08 UTC

This package is auto-updated.

Last update: 2022-06-19 05:00:50 UTC


README

Simple implementation of CommandBus and QueryBus that just works.

The CommandBus and QueryBus usage is very similar. The main difference is that the commands don't return a value and the queries return an optional one.

Build Status Scrutinizer Code Quality Code Coverage

Cutcsa 104 paso carrasco

Example

Usage in Symfony:

First you need to add your command handlers and query handlers to a container implementing PSR-11. This container has to be injected into the CommandHandlerLocator and/or the QueryHandlerLocator.

Prepare the query (or the command) and dispatch it through the proper bus.

<?php
// UserController.php
class UsersController extends Controller
{
    /** @var QueryBus */
    private $queryBus;
    
    public function __construct(QueryBus $queryBus)
    {
        $this->queryBus = $queryBus;
    }
    
    /**
     * @Route("/user/{id}", name="user_data", methods={"GET"})
     */
    public function getUserData($id): Response
    {
        try {
            $userId = new UserId($id);
            $result = $this->queryBus->dispatch(new GetUserDataQuery($userId));

            if ($result->isPresent()) {
                $user = $result->getResult();
                $userSettings = $user->getSettings();

                return new JsonResponse($user);
            }

            return new JsonREsponse(
                ['message' => 'error.userNotFound'],
                JsonResponse::HTTP_NOT_FOUND
            );
        } catch (\Exception $e) {
            return new JsonResponse(
                ['message' => 'error.unexpected ' . $e->__toString()],
                JsonResponse::HTTP_INTERNAL_SERVER_ERROR
            );
        }
    }

This is how a query looks, it needs to implement query.

<?php
// GetUserDataQuery.php

class GetUserDataQuery implements Query
{
    /** @var UserId */
    private $userId;

    public function __construct(UserId $userId)
    {
        $this->userId = $userId;
    }

    public function getUserId(): UserId
    {
        return $this->userId;
    }

    public function getQueryHandler(): HandlerIdentifier
    {
        // The identifier is a string with the key of the object in the container.
        return new HandlerIdentifier(GetUserDataQueryHandler::class);
    }
}

In the handler class is where all the logic to retrieve the value of the query or to execute the command is located:

<?php
// GetUserDataQueryHandler.php
class GetUserDataQueryHandler implements QueryHandler
{    
    /** @var UserRepository */
    private $userRepository;

    public function __construct(UserRepository $userRepository)
    {
        $this->userRepository = $userRepository;
    }

    public function handle(Query $query): OptionalResult
    {
        // Do all the magic needed to get the user from repository here
        return new GetUserDataQueryOptionalResult($user);
    }
}

The optional result is a way to manage a query that can return null or a value. This is not needed for commands because they return void.

<?php
// GetUserDataQueryOptionalResult.php
class GetUserDataQueryOptionalResult extends OptionalResult
{
    public function __construct(?User $result = null)
    {
        parent::__construct($result);
    }

    public function getResult(): User
    {
        return parent::getResult();
    }
}