CQRS vs Classical n-layer application

tl;dr - when should I use CQRS design pattern and stay sane.

When building an API the typical practice is to divide it into parts, simplest would be presentation, service / business logic and data access layers

3-layer architecture

There is nothing wrong with this approach but questions araise when application becomes bloated and unreadable.I prepared simple user’s microservice with basic functionality to make case-study on it. But first things first.

CQS

CQS

It separates two main sections of our application. Queries are thoose actions which doesn’t change data (in most cases) and return something whereas commands are those which change data and return nothing (again, in most cases). Most likely that you used it even without knowing it because it’s kinda natural approach to the problem. It allows us to:

  1. Distributed responsibility. When working in team, less experienced developers may receive queries to take care about because they don’t mess up with data.
  2. Clean code. However, this argument is often overused. I’ll explained it later.

CQRS

And that’s the problem. Actions in classic CQS are handled by methods in the end. CQRS is handling them by objects, an instance of action class with one or more methods.

CQRS

Prerequisites

  1. Sequelize ORM
  2. Swagger.io (api docs)

Practise

Simple REST CRUD microservice. Additionally user can login using his name or email and view history. Application has two modules with same set of routes. The ‘simple’ variant is made with classical approach and the other one with CQRS.

Let’s look into the code:

Controller uses service with those methods:

It takes only 116 lines of code so not a big deal but will arise when scaling because of no clear way to organize code.

Implementing CQRS

Here we have two controllers:

As you may have noticed queries and commands in form of object. Second important aspect is that we have no service in this module. It’s functionality is distributed in queries and commands directories.

Each action has its handler. Let’s look into one:

It has same functionality as List() method in service.

Another example:

As you see here we have more breathing space. Method does no longer needs to be long or cut into many messing with code organization. Simillary to main() method known from C, here execute() is beeing fired when handling query.

Note that we can’t always separate read and write blocks. Login query creates history entry violating pure Separation rule. But implementing two separate routes would be an art for art.

Domain-Driven Design elements

It’s object in our project domain and aggregate root. It means that we can talk about it and manipulate on it independently. Good example is basket in online shop app. There is no sense in implementing functionality for order positions because they don’t exists without basket. We don’t create data access to as well. In simpliest implementation it could be entity class from sequalize but this practise have a lot of flaws.

In this API it could be easily omitted and Domain-Driven Design is topic for another article. I mencioned it because it is present in official Nest.js CQRS documentation as well as Event Sourcing which is not part of CQRS. I’ll be demystifying thoose terms in next article.

Summary

But things change when code begins to grow without control. There is no need to decide on one approach in scope of whole project. Simple modules may still be based on simple controller-service setup whereas more complicated, especially contaning long in implementation routes should be considered as worthy CQRS candidates.

Project is available here: https://github.com/Matii96/users-service

Thanks for reading :)

Full-stack developer with passion towards data science

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store