Queries & Handlers
Queries request data without modifying state. They are part of the CQRS pattern in Phexium's Bus Mode.
Queries
Queries define what data is requested and any filtering criteria:
final readonly class ListBooksQuery implements QueryInterface {}
final readonly class DetailBookQuery implements QueryInterface
{
public function __construct(public string $bookId) {}
}
Characteristics:
- Immutable
- Named: verb + noun + Query (e.g.,
ListBooksQuery) - May contain filters
- No side effects
Handlers
Handlers retrieve data and return QueryResponseInterface objects:
public function __invoke(ListBooksQuery $query): ListBooksResponse
{
$books = $this->bookRepository->findAll();
$bookItems = [];
foreach ($books as $book) {
$bookItems[] = [
'id' => $book->getId()->getValue(),
'title' => $book->getTitle()->getValue(),
'status' => $book->getStatus()->value,
];
}
return new ListBooksResponse($bookItems);
}
Handler responsibilities:
- Fetch data from repository
- Transform to primitive values
- Return response
Dispatching
$query = new ListBooksQuery();
$response = $this->queryBus->dispatch($query);
$viewModel = $this->presenter->present($response)->getViewModel();
Handlers are resolved by naming convention: ListBooksQuery → ListBooksHandler.
Form Rendering Queries
Some queries carry form state (pre-filled values and validation errors) for rendering create/update forms. This pattern keeps form rendering in the query pipeline, benefiting from presenters and ViewModels:
final readonly class CreateBookQuery implements QueryInterface
{
public function __construct(
public array $formValues = [],
public array $errors = []
) {}
}
The controller dispatches this query both for initial form display (empty values) and after validation failure (with values and errors). The handler echoes the form state into the response for the presenter to format.
This is an intentional trade-off: the query does not fetch domain data, but it flows through the same bus/presenter/ViewModel pipeline, ensuring consistent rendering. The demo application uses this pattern for CreateBookQuery, UpdateBookQuery, and BorrowBookQuery.
Data Transformation
Transform domain entities to primitives in handlers:
// Do: Return primitives
$bookItems[] = ['id' => $book->getId()->getValue()];
// Don't: Return domain entities
$bookItems[] = $book; // Leaks domain to presentation
See Also
- CQRS - Conceptual foundation
- Bus Mode - Architectural context
- Query Bus - Dispatching mechanism
- Presenters - Transform query response to ViewModel