bowphp/cqrs

Command Query Responsibility Segregation

Maintainers

Package info

github.com/bowphp/cqrs

pkg:composer/bowphp/cqrs

Statistics

Installs: 5 130

Dependents: 0

Suggesters: 0

Stars: 2

Open Issues: 0

1.2.2 2025-01-20 16:36 UTC

This package is auto-updated.

Last update: 2026-05-21 21:28:39 UTC


README

CQRS (Command Query Responsibility Segregation). It's a pattern that I first heard described by Greg Young. At its heart is the notion that you can use a different model to update information than the model you use to read information. For some situations, this separation can be valuable but beware that for most systems CQRS adds risky complexity.

For more information

Install

The package uses php >= 8.1

composer require bowphp/cqrs

Help

First, create the example command:

use Bow\CQRS\Command\CommandInterface;

class CreateUserCommand implements CommandInterface
{
    public function __construct(
        public string $username,
        public string $email
    ) {}
}

Create the handler here:

use Bow\CQRS\Command\CommandHandlerInterface;

class CreateUserCommandHandler implements CommandHandlerInterface
{
    public function __construct(public UserService $userService) {}

    public function process(CommandInterface $command): mixed
    {
        if ($this->userService->exists($command->email)) {
            throw new UserServiceException(
                "The user already exists"
            );
        }

        return $this->userService->create([
            "username" => $command->username,
            "email" => $command->email
        ]);
    }
}

Add command to the register in App\Configurations\ApplicationConfiguration::class:

use Bow\CQRS\Registration as CQRSRegistration;

public function run()
{
    CQRSRegistration::commands([
        CreateUserCommand::class => CreateUserCommandHandler::class
    ]);
}

Attribute-based registration

You can also map handlers to their commands/queries with PHP attributes instead of manual arrays:

use Bow\CQRS\Attribute\CommandHandler;
use Bow\CQRS\Attribute\QueryHandler;
use Bow\CQRS\Command\CommandHandlerInterface;
use Bow\CQRS\Command\CommandInterface;
use Bow\CQRS\Query\QueryHandlerInterface;
use Bow\CQRS\Query\QueryInterface;

#[CommandHandler(CreateUserCommand::class)]
class CreateUserCommandHandler implements CommandHandlerInterface
{
    public function process(CommandInterface $command): mixed
    {
        // create the user...
    }
}

#[QueryHandler(FetchUserQuery::class)]
class FetchUserQueryHandler implements QueryHandlerInterface
{
    public function process(QueryInterface $query): mixed
    {
        // fetch and return the user...
    }
}

// Register handlers once (attributes map them to the right message)
CQRSRegistration::handlers([
    CreateUserCommandHandler::class,
    FetchUserQueryHandler::class,
]);

Execute the command in the controller:

namespace App\Controllers;

use App\Controllers\Controller;
use App\Commands\CreateUserCommand;

class UserController extends Controller
{
    public function __construct(private CommandBus $commandBus) {}

    public function __invoke(Request $request)
    {
        $payload = $request->only(['username', 'email']);
        $command = new CreateUserCommand(
            $payload['username'],
            $payload['email']
        );

        $result = $this->commandBus->execute($command);

        return redirect()
            ->back()
            ->withFlash("message", "User created");
    }
}

Put a new route:

$app->post("/users/create", UserController::class);

Contributing

Thank you for considering contributing to Bow Framework! The contribution guide is in the framework documentation.

Contact

papac@bowphp.com - @papacdev

Please, if there is a bug in the project. Contact me by email or leave me a message on slack. or join us on slack