Understanding Redux as a CQRS system

CQRS — Command Query Responsibility Segregation is a pattern where we use different interfaces to read and update data. This is as opposed to CRUD — Create Read Update Delete, where we use a single interface to perform all the reads and updates.

Often, CQRS is implemented using another pattern called Event Sourcing. The Event Sourcing pattern dictates that updates to data happen based on a sequence of events. For example, the event ‘LikeButtonClicked’ will probably increment the number of likes by 1.

Fairly good introduction to CQRS can be found here and ES here.


While implementing a CQRS system, all the commands are modelled as events. So whenever a user performs any action, a command is dispatched as an event and the event is stored in a store. Whenever we want to read data, we take the data at its initial state and apply all the commands to the data to get the most up to date data. Hopefully, the below diagram does a decent job of capturing the differences between CQRS and CRUD.

CQRS(Left) vs CRUD(Right)

There are a bunch of advantages that CQRS/ES systems offer, serialisability and the ability to time travel being a few. Stuff that redux does so well.

Enough theory, time to code.


As is customary in the react world, we’ll implement a simple counter. Let us define a store as such —

let store = {
counter: 0
}

Now I want to be able to command the store to mutate itself. So let me define a couple of actions that do just that —

const increment = { type: 'INCREMENT' };
const decrement = { type: 'DECREMENT' };

I also need to specify how the store has to update itself when it comes across an action —

let counter = (state, action) => {
switch(action.type) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
}
}

Now I need a way of actually dispatching an action. I’ll write a simple dispatch function that takes an action and updates the store accordingly —

let dispatch = (action) => {
commands.reduce((intermediateStore, command) => {
let storeFragment = store[command.name]
store[command.name] = counter(storeFragment, action);
return store;
}, store);
}

The whole example would like this —

let store = {
counter: 0
}
const increment = { type: 'INCREMENT' };
const decrement = { type: 'DECREMENT' };
let counter = (state, action) => {
switch(action.type) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
}
}
let commands = [counter];
let dispatch = (action) => {
commands.reduce((intermediateStore, command) => {
let storeFragment = store[command.name]
store[command.name] = command(storeFragment, action);
return store;
}, store);
}
dispatch(increment);
dispatch(increment);
dispatch(decrement);
console.log(store);
// assert.equals(store.count, 1)
// try it out on the babel repl

From the above example, you’ll notice that the ‘counter’ function is the equivalent of a redux reducer. The ‘commands’ array is essentially an array of all the ‘reducers’. It must be pretty obvious by now why these functions are called reducers in redux terminology — its because they are passed to a reduce method.

Approaching redux from a CQRS perspective really helped me improve my understanding on it. Previously, I was barely able to appreciate the design choices behind redux and flux. But as I used redux, it became clearer to me that it has a lot of similarities with CQRS, and then everything about redux just clicked. I hope this helps in easing the learning curve for redux.

Show your support

Clapping shows how much you appreciated Swarup Karavadi’s story.