DEV Community

BhargavMantha
BhargavMantha

Posted on

CQRS

CQRS Like You Were a 5-Year-Old

Imagine you have a toy box. When you want to play with your toys, you ask your mom to open the toy box and give you the toys you want. That's like asking a question, or in computer terms, a query. You're not changing anything, just looking at what toys you have.

Now, imagine after playing, you decide to put some of your toys back in a different way. Maybe you're putting them in order of size or grouping them by type. This time, you're not just looking; you're changing how things are arranged. In computer terms, that's like a command because you're telling the toy box (or a computer system) to change something.

CQRS stands for Command Query Responsibility Segregation.

It's a fancy way of saying that when we use computers or apps, there are two main things we can do: look at stuff (queries) and change stuff (commands). CQRS is like having two separate toy boxes: one for looking at toys and another for deciding how to organize them. This way, it's easier and faster to either find your toys or organize them, because you're not mixing up looking and changing toys in the same toy box.

Now Comes the Fun Part

You have 2 different toy boxes: 1 to find and 1 to arrange. But if I change the arrangement of the toys in 1 box, won't the same need to be made in the other box as well, in order to maintain consistency? In the current case, the changes of arrangement are not going to reflect in the other toy box, right?

Absolutely Right!!!

If you change how your toys are arranged in one toy box, you'd want the other toy box to show those changes too, so everything matches up. In our CQRS world, this means when you do something in the change stuff box, someone (usually a smart part of the computer or app) has to make sure the look at stuff box knows about those changes.

Let's Continue With Our Toy Analogy

Imagine you have a magic notebook that keeps track of what you do in the change stuff box. Every time you move a toy, you write it down in the notebook. Then, there's a friendly elf who takes a look at your notebook while you're asleep, and he makes sure everything you did in the change stuff box is also done in the look at stuff box. When you wake up and decide to play again, both your boxes are up to date, and everything matches.

In computer systems, this magic notebook can be something like a list of all the changes you've made (we call these events), and the friendly elf can be a piece of software that makes sure both sides (the command side and the query side) are kept in sync. This way, whether you're looking at your data or changing it, you always see the latest version.

Let's Explain How CQRS Might Work Using Node.js, MongoDB, and Any Additional Pieces Necessary to Make It Clear

Imagine you're building a simple app where you can add toys to a collection and view your collection. In a CQRS approach, you would have two main parts: one for adding or changing toy information (the command side) and another for viewing your toy collection (the query side).

1. MongoDB Setup

You'll use MongoDB to store your toys. But, following the CQRS principle, you might have two collections:

  • Toys_Commands: This is where you add or update toy information. Think of it as the "change stuff" box.
  • Toys_Queries: This collection is optimized for reading and displaying toy data. It's your "look at stuff" box.

2. Node.js Application

Your Node.js app will have different routes or endpoints for commands (adding/updating toys) and queries (getting toy information).

Command Side

When you want to add a new toy or update an existing one, you'll use a command endpoint like POST /toys or PUT /toys/:toyId. This part of the app will interact with the Toys_Commands collection. When a toy is added or updated, the app will also send a message (let's think of it as our "magic notebook" entry) to a messaging system (like RabbitMQ or Kafka), indicating what change was made.

Query Side

The query part of the app has endpoints like GET /toys to view the toy collection. It reads from the Toys_Queries collection, which is optimized for fast, read-only access.

Keeping Things in Sync

Now, we need our friendly elf to keep the command and query sides in sync. This can be a separate service or part of your app that listens for messages about changes made on the command side (our magic notebook). When it receives a message about a change, it updates the Toys_Queries collection accordingly. This way, the next time you go to view your toys, the collection is up-to-date with all the latest changes.

Additional Pieces

Messaging System (RabbitMQ/Kafka):

This acts as the intermediary that passes messages from the command side to the service responsible for updating the query side. It ensures that all changes are queued and processed reliably.

Separate Services/Workers for Syncing:

These are specialized parts of your app (or separate apps) that listen to the messaging system and update the query side based on the command side's changes.

Example Flow

Add a Toy (Command):

You send a request to POST /toys to add a new toy. The toy is added to Toys_Commands, and a message is sent to RabbitMQ noting the addition.

Syncing:

A separate service listens to RabbitMQ, receives the message about the new toy, and adds this toy to Toys_Queries.

View Toys (Query):

When you request GET /toys, the app reads from Toys_Queries, showing you the latest collection, including the toy you just added.

This setup, with Node.js, MongoDB, and a messaging system, helps keep your app organized according to CQRS principles, separating the concerns of modifying data and reading it, while also ensuring consistency between those views.

Top comments (0)