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.

If you are using autowiring, all your classes are registered in the container under their class names. This means Ecotone can automatically resolve them without any extra configuration.

If your service is registered under a different name in the Dependency Container, you can use ClassReference to point Ecotone to the correct service:

#[ClassReference("ticketService")]
class TicketService

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:

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.

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.

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.

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.

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?