Commands & Handlers
Commands represent intentions to change state. They are part of the CQRS pattern implemented in Phexium's Bus Mode.
Commands
Commands are immutable data objects carrying all information needed for a write operation.
final readonly class CreateBookCommand implements CommandInterface
{
public function __construct(
public IdInterface $id,
public string $title,
public string $author,
public string $isbn
) {}
}
Characteristics:
- Immutable (
readonlyclass) - Named imperatively: verb + noun (e.g.,
CreateBookCommand) - Self-contained with all required data
- Handlers return
void
Handlers
Each command has exactly one handler that executes the business logic:
public function __invoke(CreateBookCommand $command): void
{
$book = new Book(
$command->id,
Title::fromString($command->title),
Author::fromString($command->author),
ISBN::fromString($command->isbn)
);
$this->bookRepository->save($book);
$this->eventBus->dispatch(new BookCreatedEvent(
$this->idGenerator->generate(),
$this->clock->now(),
$book,
));
}
Handler responsibilities:
- Create/reconstitute domain entities
- Persist via repository
- Dispatch domain events
Type safety: Handlers use typed __invoke() with the concrete command type, providing compile-time type safety without runtime checks.
Dispatching
$command = new CreateBookCommand($id, $title, $author, $isbn);
$this->commandBus->dispatch($command);
Handlers are resolved by naming convention: CreateBookCommand → CreateBookHandler.
Best Practices
- Keep commands immutable
- Include all necessary data in the command
- Dispatch events after successful persistence
- Don't return values (use queries for reads)
See Also
- CQRS - Conceptual foundation
- Bus Mode - Architectural context
- Command Bus - Dispatching mechanism
- Event Bus - Event dispatching from handlers
- Domain Events - Events dispatched after persistence