Getting started with CQRS in PHP
CQRS stands for Command Query Responsibility Segregation. It's a pattern that separates how an application reads data from how it writes data. This approach can help structure code more clearly, especially in systems that deal with complex business logic or need to scale certain operations differently.
In PHP, CQRS is not a built-in feature. It's a way to organize your code, often supported by using command buses, query handlers, or simple service classes. The main idea is to treat commands and queries as different things, handled by different objects.
Handling actions with Commands
Commands represent intent to change the state of the system. They usually don't return anything and are used to carry out operations like creating a user, updating an order, or sending a notification.
Here's a simple PHP example of a command class:
// CreateUserCommand.php
class CreateUserCommand
{
public function __construct(
public string $email,
public string $password
) {}
}
To process this command, a handler is needed. The handler is responsible for the actual business logic related to that command. It could talk to the database, trigger events, or do whatever is needed.
// CreateUserCommandHandler.php
class CreateUserCommandHandler
{
public function handle(CreateUserCommand $command): void
{
// Example of simple logic
$hashedPassword = password_hash($command->password, PASSWORD_BCRYPT);
// Save in DB and perform required actions
// Output used for demonstration
echo sprintf('User created with email: %s', $command->email);
}
}
This way, the command just holds data, and the handler deals with the actual processing. The structure makes it easy to test and reason about what's happening in the system.
Handling data fetching with Queries
Queries are used to retrieve data without causing any changes to the application's state. They return information only, with typical examples including fetching a user by ID or listing all orders for a customer.
Here's a basic PHP query class:
// GetUserByEmailQuery.php
class GetUserByEmailQuery
{
public function __construct(public string $email) {}
}
Similar to commands, queries have their own handlers. The handler for a query focuses only on fetching and returning data, nothing more.
// GetUserByEmailQueryHandler.php
class GetUserByEmailQueryHandler
{
public function handle(GetUserByEmailQuery $query): array
{
// Call the DB and get actual user information
// Mocked response used for demonstration
return [
'email' => $query->email,
'name' => 'Test User'
];
}
}
Using separate handlers for queries makes it easier to fine-tune how data is fetched, especially when read requirements start to differ from write needs.
When to consider using CQRS
CQRS doesn't need to be applied across the entire application. In many cases, a simpler structure is enough, especially for smaller projects or areas with minimal complexity. However, CQRS becomes especially useful when the system starts to show certain patterns or challenges, such as:
Complex business logic that benefits from clearly separated responsibilities
Different performance needs between reads and writes, where reads may need to scale independently
Distinct models for reading and writing, especially when the data returned to users is formatted differently from how it's stored
Clear separation of concerns, making the codebase easier to test, understand, and maintain
Collaboration across teams, where separating reads and writes allows different parts of the system to evolve independently
In these situations, applying CQRS can help improve structure, reduce unintended side effects, and make future changes more manageable. In smaller projects, it might feel like extra work. But in larger systems, it can reduce mess and help teams work more independently on commands or queries without stepping on each other's code.
Conclusion
CQRS isn't something you have to fully commit to across every part of your app. It's a practical way to separate responsibilities and keep logic focused. By giving commands and queries their own space, your code can stay a little cleaner, especially as the system grows or becomes more complicated.
0 Comments