Dispatching
Dispatcher::dispatch(object $cmd): mixed reads the #[Handler] attribute from the command’s class, walking up to parent classes if needed, resolves that handler from the container, and calls handle(). A command with no handler throws Shared\Infrastructure\CommandBus\Exception\NoHandlerFoundException.
There is no queue and no middleware: the handler runs synchronously, in the current request.
A command
src/Category/Application/Commands/CreateCategoryCommand.php
A handler
src/Category/Application/CommandHandlers/CreateCategoryCommandHandler.php
Commands and queries
Both use the bus. The naming convention makes the intent clear:Named constructors
Some commands offer alternative constructors for lookups by something other than an ID:Generators
A handler may return aGenerator, which is how streamed AI responses work: the handler yields parts as the provider produces them, and the request handler streams them to the client. See Streaming responses.
Errors
Handlers throw domain exceptions rather than returning error values:Using the bus from a plugin
Plugins dispatch core commands, and define their own:Conventions
One command, one handler, one responsibility.
Commands hold data and convert it into value objects; they contain no logic.
Handlers dispatch a domain event after a change, so other code can react.
Handlers return entities or iterators, not formatted output. Formatting belongs in a resource.
Nothing is flushed inside a handler unless it genuinely must be.