Skip to content

/symfony-cqrs-handlers

Symfony CQRS command/query handlers — commands, queries, handlers, bus configuration, use cases. Triggers on: command, query, handler, CQRS, bus, use case, command handler, query handler, message bus

shell
$ npx -y skills add aligundogdu/symfony-hexagonal-skill --skill symfony-cqrs-handlers --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.
  • You can call itInvoke it directly when you want it.
  • Slash command/symfony-cqrs-handlers
How auto-invocation works

Context preview

The summary Claude sees to decide when to auto-load this skill.

Symfony CQRS command/query handlers — commands, queries, handlers, bus configuration, use cases. Triggers on: command, query, handler, CQRS, bus, use case, command handler, query handler, message bus

SKILL.md

symfony-cqrs-handlers.SKILL.md
description: "Symfony CQRS command/query handlers — commands, queries, handlers, bus configuration, use cases. Triggers on: command, query, handler, CQRS, bus, use case, command handler, query handler, message bus"

Symfony CQRS Handlers

You are an expert in CQRS (Command Query Responsibility Segregation) within Symfony hexagonal architecture.

When to Activate

  • User wants to create a command or query
  • User needs a handler for a use case
  • User asks about CQRS patterns or message bus configuration
  • User mentions "use case", "action", "operation" in application context

Command Pattern

Commands represent write operations (create, update, delete). They are DTOs dispatched to the command bus.

Rules

  • `final readonly class` — immutable after construction
  • Named as imperative verb: `RegisterUser`, `PlaceOrder`, `CancelSubscription`
  • Contains only primitive types and value objects — no entities
  • Handler returns `void` or a scalar identifier (string ID)
  • One handler per command

Template

namespace App\Application\{Module}\Command;

final readonly class {ActionVerb}{Entity}
{
    public function __construct(
        public string $param1,
        public string $param2,
        // only primitives and simple types
    ) {
    }
}

Handler Template

namespace App\Application\{Module}\Command;

use App\Domain\{Module}\Port\{Repository}Interface;
use Symfony\Component\Messenger\Attribute\AsMessageHandler;

#[AsMessageHandler(bus: 'command.bus')]
final readonly class {ActionVerb}{Entity}Handler
{
    public function __construct(
        private {Repository}Interface $repository,
    ) {
    }

    public function __invoke({ActionVerb}{Entity} $command): void
    {
        // 1. Reconstruct/create domain objects
        // 2. Execute business logic
        // 3. Persist via port
        // NO side-effects here — use domain events
    }
}

Query Pattern

Queries represent read operations. They return DTOs, never domain entities.

Rules

  • `final readonly class` — immutable
  • Named descriptively: `GetUserById`, `ListActiveOrders`, `SearchProducts`
  • Handler MUST return a DTO or array of DTOs
  • Handler NEVER modifies state
  • May use read-optimized ports (separate from write ports)

Template

namespace App\Application\{Module}\Query;

final readonly class {GetDescription}
{
    public function __construct(
        public string $identifier,
        // filter/pagination params
    ) {
    }
}

Handler Template

namespace App\Application\{Module}\Query;

use App\Application\{Module}\DTO\{Entity}DTO;
use App\Domain\{Module}\Port\{Repository}Interface;
use Symfony\Component\Messenger\Attribute\AsMessageHandler;

#[AsMessageHandler(bus: 'query.bus')]
final readonly class {GetDescription}Handler
{
    public function __construct(
        private {Repository}Interface $repository,
    ) {
    }

    public function __invoke({GetDescription} $query): ?{Entity}DTO
    {
        $entity = $this->repository->findById($query->identifier);
        if ($entity === null) {
            return null;
        }

        return {Entity}DTO::fromEntity($entity);
    }
}

DTO Pattern

namespace App\Application\{Module}\DTO;

final readonly class {Entity}DTO
{
    public function __construct(
        public string $id,
        public string $field1,
        public string $field2,
    ) {
    }

    public static function fromEntity(/* entity */): self
    {
        return new self(
            id: (string) $entity->id(),
            field1: $entity->field1(),
            field2: $entity->field2(),
        );
    }
}

References

See `references/` for detailed guides:

  • `command-patterns.md` — Full command examples with validation
  • `query-patterns.md` — Query patterns with pagination and filtering
  • `bus-configuration.md` — Messenger bus setup and middleware
Read more
Read it on GitHub ↗
Ships withsymfony-hexagonal-skill

A Claude Code plugin that enforces hexagonal architecture (ports & adapters) in Symfony projects. Works with both new projects (full scaffolding) and existing projects (progressive, module-by-module refactoring).

Get the whole plugin, auto-invoked

Other skills on symfony-hexagonal-skill.