Skip to content

momo-framework/bus

Repository files navigation

Momo Framework

momo-framework/bus

Synchronous in-process CQRS buses for Momo Framework. Provides a strictly typed interface for Command and Query dispatching within a single request cycle.

CI Latest Version Total Downloads PHP Version License Coverage PHPStan


Overview

momo-framework/bus provides two lightweight, in-process buses for separating write and read operations in your application — following the CQRS pattern.

Bus Purpose Returns
CommandBus Write operations — create, update, delete void
QueryBus Read operations — fetch, list, search mixed

Both buses are always synchronous — handlers execute in the same process, in the same request cycle. For async processing, use momo-framework/queue.

Both buses enforce a one-handler-per-message contract. Registering a second handler for the same message class silently overwrites the first.


Requirements

  • PHP >= 8.5
  • momo-framework/kernel

Installation

Auto-discovered by Momo Framework via PackageManifest — no manual registration needed.

composer require momo-framework/bus

Core concept

HTTP / gRPC / CLI / GraphQL
           │
           ▼
    Controller / Resolver         (delivery layer — knows about protocol)
           │
           │  new CreateOrderCommand(...)
           ▼
       CommandBus                 (this package)
           │
           ▼
    CreateOrderHandler            (application layer — knows about domain)
           │
           ▼
    Domain / Infrastructure       (pure business logic)

Adding a new protocol (gRPC, GraphQL, CLI) means writing a new adapter that dispatches the same Command or Query. Handlers never change.


Usage

Commands — write side

Define a command as a simple readonly value object:

final readonly class CreateOrderCommand implements CommandInterface
{
    public function __construct(
        public string $customerId,
        public array  $items,
    ) {}
}

Define a handler that performs the operation:

final class CreateOrderHandler implements CommandHandlerInterface
{
    public function __construct(
        private readonly OrderRepository $orders,
        private readonly QueueInterface  $queue,
    ) {}

    public function handle(CommandInterface $command): void
    {
        assert($command instanceof CreateOrderCommand);

        $order = Order::create($command->customerId, $command->items);
        $this->orders->save($order);

        // Heavy work goes to the queue — bus stays synchronous
        $this->queue->push(new SendOrderEmailJob($order->id));
    }
}

Dispatch from a controller:

$commandBus->dispatch(new CreateOrderCommand($customerId, $items));
// returns void — fire and forget

Queries — read side

Define a query:

final readonly class GetOrderQuery implements QueryInterface
{
    public function __construct(
        public string $orderId,
    ) {}
}

Define a handler that returns data:

final class GetOrderHandler implements QueryHandlerInterface
{
    public function __construct(
        private readonly OrderRepository $orders,
    ) {}

    public function handle(QueryInterface $query): mixed
    {
        assert($query instanceof GetOrderQuery);

        return $this->orders->findById($query->orderId);
    }
}

Ask from a controller:

$order = $queryBus->ask(new GetOrderQuery($orderId));
// returns whatever the handler returns

Registering in a Module ServiceProvider

final class ShopServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->singleton(CreateOrderHandler::class);
        $this->singleton(GetOrderHandler::class);
    }

    public function boot(): void
    {
        $this->app->make(CommandBusInterface::class)
            ->register(CreateOrderCommand::class, $this->app->make(CreateOrderHandler::class));

        $this->app->make(QueryBusInterface::class)
            ->register(GetOrderQuery::class, $this->app->make(GetOrderHandler::class));
    }
}

Development

# install dependencies
composer install

# run tests
composer test

# run tests with coverage report (requires PCOV)
composer test:coverage

# static analysis — PHPStan level 10
composer stan

# code style check
composer lint

# code style fix
composer lint:fix

# rector — check for upgrades
composer rector:check

# run full CI pipeline locally
composer ci

CI pipeline

composer ci
  ├── lint          php-cs-fixer --dry-run
  ├── stan          phpstan level 10
  ├── rector:check  rector --dry-run
  └── test          phpunit

Part of Momo Framework — a high-performance, modular PHP framework for building resilient distributed systems.

About

Synchronous in-process CQRS buses for Momo Framework. Provides a strictly typed interface for Command and Query dispatching within a single request cycle.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages