In this posts, we’ll show you how we use the CQRS “web scale” pattern to achieve scalability and flexibility in our back office software. This is needed as we rely heavily on our back office to offer our customers the best available service.
For example, several parts of our online shop do requests on our stock levels and warehouse configuration to determine which delivery options are available for a particular product. It naturally follows that the services that know our stocks levels and warehouse configuration have to be scaled to handle these volumes. To enable this we don’t just need more hardware. We also need to apply patterns to our services to create a proper structure.
CQRS, apply with caution
CQRS is short for Command Query Responsibility Segregation. At the core of CQRS is the notion that you can alter data with a different model than the one used to query data. Updating and reading information have different requirements on a model. There are enough cases where it serves to split these. The downside of this separation is that it introduces complexity. So this pattern should always be applied with caution.
The most common approach for people to interact with data in a service or system is CRUD. Create, Read, Update and Delete are the four basic operations on persistent storage. Although there exist other variations like BREAD and MADS, CRUD is widely used in systems development.
When a need arises for multiple representations of information, the CRUD model to access data is usually split over several layers. If you need multiple representations of information and allow users to interact with them in different ways, CRUD quickly becomes overly complicated and needs to be extended. That is where CQRS can be helpful.
The perks of CQRS
In most complex domains, using the same model for commands and queries only leaves you with an even more complex model. A complex model that at some point fails both the command (mutating) and the query part. And who wants that?
Where do we use it at bol.com?
We happily apply CQRS in our Inventory Management which updates our stock levels and serves them to several services in our landscape, including our online shop. As you can imagine the traffic our online shop results in quite some load for a "back office" service.
Stock level updates come from our warehouse management, think of shipments, received goods. or reservations based on customer orders. Stock level queries originate in the online shop, check-out and fulfilment network. It is easy to see these queries are very different from the updates. And the number of queries far outreaches the number of updates.
Given these different requirements we decided to split command (updates) and query for inventory management. All updates are handled by a technically isolated part of the service. Stock levels are served by other services by another isolated part.
The part that handles the updates has several models. The incoming changes like the shipments and received goods have to be handled in for example stock mutations, stock levels and stock valuation. These models receive updates and process them to a new stock level and stock valuation. Once a new stock level is calculated, it is published on a messaging queue to the query part. This message is also consumed by other services that need these.
The query part is a simple single table. The messages from the update part are stored in this table and there is no additional logic or processing. Queries from other services are handled by a REST interface. Due to this design, this call has a very high cache hit ratio. Which of course leverages performance.
By using CQRS we achieved web-scale performance for stock processing and other services in our back office. As you saw the implementation was quite simple and it could be built relatively fast. All this enabled us to offer our customers the best available service.