CQRS Introduction - Commands
Commands CQRS PHP
In this section, we will look at how to use Commands, Events, and Queries. This will help you understand the basics of Ecotone’s CQRS support and how to build a message-driven application.
Command Handlers are methods where we typically place our business logic, so we’ll start by exploring how to use them.
Handling Commands
Any service available in your Dependency Container can become a Command Handler.
Command Handlers are responsible for performing business actions in your system.
In Ecotone-based applications, you register a Command Handler by adding the CommandHandler attribute to the specific method that should handle the command:
class TicketService
{
#[CommandHandler]
public function createTicket(CreateTicketCommand $command) : void
{
// handle create ticket command
}
}In the example above, the #[CommandHandler] attribute tells Ecotone that the "createTicket" method should handle the CreateTicketCommand.
The first parameter of a Command Handler method determines which command type it handles — in this case, it is CreateTicketCommand.
In Ecotone, the class itself is not a Command Handler — only the specific method is. This means you can place multiple Command Handlers inside the same class, to make correlated actions available under same API class.
Sending Commands
We send a Command using the Command Bus. After installing Ecotone, all Buses are automatically available in the Dependency Container, so we can start using them right away. Before we can send a Command, we first need to define it:
All Messages (Commands, Queries, and Events), as well as Message Handlers, are just plain PHP objects. They don’t need to extend or implement any Ecotone-specific classes. This keeps your business code clean, simple, and easy to understand.
To send a command, we use the send method on the CommandBus.
The command gets automatically routed to its corresponding Command Handler
Sending Commands with Metadata
We can send commands with metadata (also called Message Headers) through the Command Bus. This lets us include additional context that doesn't belong in the command itself, or share information across multiple Command Handlers without duplicating it in each command class.
And then to access given metadata, we will be using Header attribute:
The #[Header] attribute tells Ecotone to fetch a specific piece of metadata using the key executorId. This way, Ecotone knows exactly which metadata value to pass into our Command Handler.
If we use Asynchronous Command Handler, Ecotone will ensure our metadata will be serialized and deserialized correctly.
Injecting Services into Command Handler
If we need additional services from the Dependency Container to handle our business logic, we can inject them into our Command Handler using the #[Reference] attribute:
In case Service is defined under custom id in DI, we may pass the reference name to the attribute:
Sending Commands via Routing
In Ecotone we may register Command Handlers under routing instead of a class name.
This is especially useful if we will register Converters to tell Ecotone how to deserialize given Command. This way we may simplify higher level code like Controllers or Console Line Commands by avoid transformation logic.
Ecotone is using message routing for cross application communication. This way applications can stay decoupled from each other, as there is no need to share the classes between them.
Routing without Command Classes
There may be cases where creating Command classes is unnecessary boilerplate, in those situations, we may simplify the code and make use scalars, arrays or non-command classes directly.
Ecotone provides flexibility which allows to create Command classes when there are actually needed. In other cases we may use routing functionality together with simple types in order to fulfill our business logic.
Returning Data from Command Handler
Sometimes we need to return a value immediately after handling a command. This is useful for scenarios that require instant feedback—for example, when processing a payment, we might need to return a redirect URL to guide the user to the payment gateway. Ecotone's allows for returning data from Command Handler, that will be available as a result from your CommandBus:
The returned data will be available as result of the Command Bus.
Keep in mind that return values only work with synchronous Command Handlers. For asynchronous handlers, we can't return values directly because the command is processed in the background—instead, we'd use events or callbacks to communicate results back to the user when processing completes.
Sending Commands with deserialization
When any Serialization mechanism is configured (For example JMS), we can let Ecotone do the deserialization in-fly, so we don't need to both with doing custom transformations in the Controller:
Last updated
Was this helpful?