Search by

gian-tiaga / spiral-cqrs

gian_tiaga

CQRS bus package for Spiral applications

Package info

github.com/falur/spiral-cqrs

pkg:composer/gian-tiaga/spiral-cqrs

Statistics

Installs: 16

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.1.0 2026-09-14 14:37 UTC

This package is auto-updated.

Last update: 2026-09-14 15:20:00 UTC


README

gian-tiaga/spiral-cqrs добавляет в Spiral-приложение простые шины команд и запросов.

Пакет даёт:

  • CommandBusInterface для сценариев, которые меняют состояние;
  • QueryBusInterface для сценариев чтения;
  • атрибут #[Transactional] для транзакций команд;
  • атрибут #[LogOperation] для debug-логов выполнения;
  • PHPStan-правило, которое требует передавать handler как $handler->handle(...).

Установка

composer require gian-tiaga/spiral-cqrs:^0.1.0

Подключите bootloader в приложении:

use GianTiaga\SpiralCqrs\Bootloader\CqrsBootloader;

protected const LOAD = [
    CqrsBootloader::class,
];

Bootloader регистрирует CommandBusInterface и QueryBusInterface.

Command

Command — это объект с входными данными сценария. Handler выполняет сценарий.

use GianTiaga\SpiralCqrs\CommandBusInterface;
use GianTiaga\SpiralCqrs\Attribute\LogOperation;
use GianTiaga\SpiralCqrs\Attribute\Transactional;

final readonly class CreatePostCommand
{
    public function __construct(
        public string $authorId,
        public string $title,
        public string $body,
    ) {}
}

final readonly class CreatePostHandler
{
    public function __construct(
        private PostRepository $postRepository,
    ) {}

    #[Transactional]
    #[LogOperation(name: 'post.create')]
    public function handle(CreatePostCommand $command): string
    {
        $post = Post::create(
            authorId: $command->authorId,
            title: $command->title,
            body: $command->body,
        );

        $this->postRepository->add($post);

        return $post->id();
    }
}

$postId = $commandBus->dispatch(
    command: new CreatePostCommand(
        authorId: $authorId,
        title: $title,
        body: $body,
    ),
    handler: $createPostHandler->handle(...),
);

#[Transactional] работает только для command handler. Если поставить его на query handler, package PHPStan-правило сообщит об ошибке.

Query

Query читает данные и не должен менять состояние приложения.

use GianTiaga\SpiralCqrs\QueryBusInterface;

final readonly class GetPostQuery
{
    public function __construct(
        public string $postId,
    ) {}
}

final readonly class GetPostHandler
{
    public function __construct(
        private PostReadRepository $postReadRepository,
    ) {}

    public function handle(GetPostQuery $query): PostView
    {
        return $this->postReadRepository->getView($query->postId);
    }
}

$post = $queryBus->dispatch(
    query: new GetPostQuery(postId: $postId),
    handler: $getPostHandler->handle(...),
);

Логи и исключения

#[LogOperation] пишет debug-лог перед запуском handler и после завершения. Сообщения логов и служебные исключения пакета написаны на русском языке.

PHPStan

Чтобы проверять вызовы dispatch(), подключите extension:

includes:
    - vendor/gian-tiaga/spiral-cqrs/extension.neon

Разрешено:

$commandBus->dispatch(
    command: $command,
    handler: $handler->handle(...),
);

Запрещено:

$commandBus->dispatch(command: $command, handler: fn() => null);
$commandBus->dispatch(command: $command, handler: [$handler, 'handle']);
$commandBus->dispatch(command: $command, handler: $handler);

Идентификаторы ошибок:

  • gianTiaga.spiralCqrs.handlerCallableRequired;
  • gianTiaga.spiralCqrs.transactionalQueryHandler.

Локальная разработка

Чтобы править пакет рядом с приложением, подключите его каталог path repository — путь считается от корня приложения:

{
  "repositories": [
    {
      "type": "path",
      "url": "../spiral-cqrs",
      "options": {
        "symlink": true
      }
    }
  ]
}

Проверки пакета:

composer install
composer test
composer phpstan