CQRS Principle with Symfony Messenger

Mert Simsek
Beyn Technology
Published in
8 min readSep 29, 2022

I’d like to continue my blog posts with CQRS, which is one of the architectural design patterns. I perceive that there is a pattern type that is difficult to understand. And I will try to explain with superficially simple examples. Have fun!

CQRS (Command Query Responsibility Segregation)

CQRS is an architectural design model whose main purpose is based on the division of write and read responsibilities. We can think of commands as code blocks that modify data (create, update, delete), and queries as code blocks that read data. In fact, it is exactly this; Command structures are structures that change a state, query structures are structures that read the state. In this article, we’ll go around the concepts of command and query and try to understand what it entails. CQRS basically divides the actions of a system/application into two categories as command and query, so command or query is designed to perform its own responsibility. Command objects that modify a state. Query is objects that read data. CQRS says that their responsibilities should be separated. The most basic idea of the CQRS structure is that a method can return a value or modify an object. He does not accept to do both together and evaluates them differently. Now for those who want to look a little deeper, let’s continue.

https://enterprisecraftsmanship.com/posts/types-of-cqrs/
  • Commands: Changes the state of the object or system.
  • Queries: It only returns the result and does not change the state of any object or system.

CQRS Integration

Single DB

The same database is used for reading and writing to the database. This means that Command and Query are sent to the same destination. In such non-clustered architectures, cache clean events are thrown by the ORM or Unit of Work after the write operation.

It is the most preferred application type in the phased transition to the CQRS architecture.

Multiple DBs

Different databases are used for database writing and reading. This means that Commands are sent to write DB and Queries are sent to read DB. It is sufficient to have a simple Master-Slave(s) structure to be implemented. Write operations are sent to the Master node and automatic synchronization is provided with the slave nodes. Slave nodes are used for reading operations. The only negative side of this application is that in case of a possible sync problem, old data is presented to the users.

Event Source

Same or different DBs can be used in this application, the focus is on storing events. In general use, we have the final version of the data row base, document base etc. we keep But we cannot know about the state of this data at time T, we can only know its final state. In order to have information about the state of the data in a past moment, change log etc. We need to implement methods. We record events with Event Sourcing and we need to combine all the records to get a final data. But the beautiful part is that we can get to the state of the data at any time we want. This means we now have the ability to rewind/fast forward. Commands should point to the insert action, if rollback is required, if 100 values ​​are added, we should open a new loss and save it as -100. Queries, on the other hand, present data by combining records at any time.

Command

It is used to add new data or update existing data. For example; Insert, Update, Delete. It does not return data. If there is no command, the state of the system remains unchanged. Command types should not return any value. For example;

Query

It is used to retrieve data. It returns only the specified resource and does not make any changes to the resource. Similarly, the only way to do a read operation is with the Query type. They cannot change the state of the system. For example;

We don’t only separate our entity DB queries, we also separate our methods in our application layer with CQRS. In addition to these, we can separate databases in the form of command-query. I will talk about 3 different benefits and critical issues when applying CQRS, and I will talk about how we make these distinctions and what advantages they provide.

https://martinfowler.com/bliki/CQRS.html

So what advantage does CQRS give us?

1-) Separation of concerns

Commands either changes the state of an object, has side-effects or fulfills both criteria at the same time. Queries read and returns the information about the state of an object from different data stores and they don’t change the state of the system (never modify a database.).

2-) Maintenance and flexibility

Using different models to update and read domain data, you can end up with very simple Queries and add more complex logic only to the Commands if you need it, managing and optimizing them independently.

3-) Scalability

Working with only one large database, can cause you a lot of problems when handling the growing number of complex reads and writes, resulting in errors, bottlenecks, etc.

Symfony Messenger

From now on, we’re able to forge how we can manage the configuration of the Symfony Messenger component to picture with a stable CQRS system. Symfony Messenger provides a message bus with the ability to send messages and then handle them immediately in your application or send them through transports (e.g. queues) to be handled later.

composer require messenger

Command Bus

Command and “CommandHandler” are only plain interfaces that are describing their jobs. “CommandBus” is an interface where the DI stays. We are able to invert the dependencies in straightforward way. Then, we got an implementation that is using the Symfony Messenger component. We will never use strictly this implementation of the “CommandBus”.

Query Bus

It’s time to handle some queries for reading. For this we should have other some interfaces.

“Query” and “QueryHandler” are getting 0 defined methods/functions as you see. So, “QueryBus” is familiar to “CommandBus”. It is a file that the point of dependency inversion and abstraction of Messenger. Another reminder — don’t use directly the Messenger implementation of “QueryBus”, do it only via “QueryBus” interface. Look at the “MessageBusInterface” parameter — the name is $queryBus because it indicates name of the bus (query.bus).

There is only 1 difference between “QueryBus” and “CommandBus” files. In a “MessengerQueryBus”, We’ve had “HandleTrait”. Because as default, buses are not giving the results and this way makes quicker to fetch results from the buses. Actually this is a bad way and PHP doesn’t have generic types unfortunately. So creating “QueryBuses” would be much more elegant. In the real world, we just need to define “mixed” return type in a “docblock”.

Our classes as far as I think they are flawless. Configuring services is not the most exciting activity, but anyway, let’s dive into this topic. First things first, we have to define buses and their transports. Most simply, you can add these lines to your messenger YAML configuration.

We have configured buses and transports. Now, let’s tag our handlers to specific buses using Messenger method.

Command + Handler

Let’s think we’re in the e-Commerce logic, so we should have a use-case as creating an item if the given item name is unique. This case could be implemented using simple CRUD mechanisms but this is our only instance. We start by creating a command and his handler. In this case, we’re using UUIDs for identifying the resources so IDs are strings.

We presume that we already got the item factory class and the repository of items. They aren’t clarified yet. The command model figured out by the Command+Handler is controlling if the claimed item name is unique and any other item doesn’t exist with this name. If exists then we’re throwing an exception and discarding creating item process.

Query + Handler

We have already our command done. Let’s tackle creating a query side for use in controllers, validators, forms, etc. Maybe you remember what you have read a few seconds before: we’re throwing an exception when the given item name is already taken. We don’t want to face users with this exception moreover we need to do a pre-check before dispatching the command. That’s another reason to create a read-model and give the user a satisfying experience with nice information about failed action.

Another way to implement a query handler is to create an abstraction of the connection. I think that’s an unnecessary level of abstraction in this case and your query-handler will just forward the parameter to it so it’s an anti-pattern in the terms of modular programming..

Usage

Let’s presume that we have a controller that is using our newly created methods. We want to create the item with a specific name. In the first step, we need to do input validation and then business validation. We can have a Symfony Controller and use these handlers to add or fetch items.

We can quickly build a CQRS pattern with Symfony Messenger making custom Message Buses and defining a model that can be reused along the project. CQRS can provide us to separate operations and searching concerns into descriptive Command/Query classes for building better-isolated processes making classes open for changes. In our controller, item creation is worked in a separated bus and if we need to check its name property we run query(read)handler and it’s worked totally in a different bus. Ultimately, what we have? Let’s say item name control will be used in a different place 10x times so we need to have a scalability for query handle. In this way, Command will be divided from scaling and we can scale our query services independently. For further, let’s say teams are divided by DDD. You can make related team assigned as a command and a query also. The key is harmony here. CQRS is a solution especially for the Application Layer. Another is that you do not have to separate the databases, you should determine it according to your needs, and if you are going to separate, you can still choose many DBs for your needs on the Read side.

To Sum Up

I hope it was useful. Software development with CQRS is a costly method. If used in the right place, it will be easier to maintain and sustain the developed system. For this reason, as in every software development pattern, it should be used where necessary. When we look at its advantages, it is pleasing to the eye, but it is not the type to be used to say let’s use CQRS. The project itself will tell when it will be used. Additionally, for PHP, Messenger component is not best way but it’s a good choice to implement CQRS for your PHP applications. Because it can handle this case easily and clear. CQRS gives us benefits in designing more efficient, understandable and developable systems and optimizing performance. I will continue my research on event sourcing and consistency used with CQRS and will be sharing the new information I have obtained in my next article.

Resources

--

--

Mert Simsek
Beyn Technology

I’m a software developer who wants to learn more. First of all, I’m interested in building, testing, and deploying automatically and autonomously.