Redux among us
This is one of the most popular
state-manager`s .
It is easy to use, transparent and predictable. With it, you can organize the storage and modification of data. And if we assume that the
action `s and
reducer`s are part of
redux` a, then we can say without exaggeration that he is the holder of all the domain logic (aka business logic) of our applications.
Is everything so rosy?
For all its simplicity and transparency, the use of
redux `and has several disadvantages ...
The data in the
state `
redux` and lies in a simple
javascript object and can be obtained in the usual way, you just need to
know the way .
')
But how do we organize this data? There are only 2 options: a
flat list and a
hierarchical model .
A flat list is a great option for an application in which there is only, for example, a counter ... For something more serious, we need a hierarchical structure. In addition, each level of data will have less knowledge than the previous one. Everything is logical and understandable, but the
path to the data becomes complex .
Exampleconst dataHierarchy = { user: { id, name, experience, achievements: { firstTraining, threeTrainingsInRow, }, }, training: { currentSetId, status, totalAttemptsCount, attemptsLeft, mechanics: { ... }, }, };
Here the user key is stored under the user key, in particular
achivements . But achievements do not need to know anything about the rest of the user data.
In the same way, a specific workout mechanic doesn’t need to know how many attempts a user has left - this is data training in general.
The presence of a hierarchical data structure and the absence of a modular approach to this data leads to the fact that
in every place where this data is used,
it is necessary to know the full path to it. In other words, it creates
connectivity of the data storage structure and their display structures and leads to difficulties with the path refactor and / or reorganization of the data structure.

IDE magic does not helpWe can say that now there are powerful IDEs that change paths with one command, but little can change several nested keys of an object or understand that part of the path lies in a variable.
Another difficulty is testing. Yes, a separate article is devoted to the documentation for
redux testing, but now it is not about testing individual artifacts like
reducer`s and
action-creater`s .
Data,
action`s and
reducer`s are usually interrelated. And one tree of logically related data is often served by several
reducer `s, which need to be tested, including together.
Add to this list the
selector`s , the results of which depend in particular on the work of the
reducer`s ...
In general, you can test all this, but you will have to deal with a pack of artifacts, connected only by logic and agreements.
And what to do if we invented a structure, for example, with user data, including lists of friends, favorite songs and something else, as well as the functionality to change them through the
action `
s- reducer` s. Perhaps we even wrote a pack of tests for all this functionality. And now in the next project we need the same ...
How to make a cheap code?Search for a solution
In order to figure out how to preserve the advantages of
redux `a and get rid of the described shortcomings, you need to understand what depends on what in the data life cycle:
- Action `s report about the events, custom and not only
- Reducer`s react to action`s and change or do not change the data state
- Data change is an event in itself and may cause other data to change.
The controller in this case is an abstraction that processes both user actions and data changes in the
store `s. It is not at all necessary for it to be a separate class, as a rule, it is spread over components.
Combine the entire redux zoo into a black box
But what if we wrap the
action `s,
reducer`s and
selector`s in one module and teach it not to depend on a specific path for the location of its data?
What if all
user `s actions, for example, will be performed by calling the instance method:
user.addFriend (friendId) ? And the data obtained through getter:
user.getFriendsCount () ?
What if we can import all user functionality by simple
import ?
const userModule from 'node_modules/user-module';
Conveniently? Especially considering that for this you do not need to write a bunch of extra code:
The npm package
redux-module-creator provides all the functionality to create
loosely coupled, reusable and tested redux modules .
Each module consists of
controller `a,
reducer` and and
action`s and has the following features:
- integrates into the store through a call to the integrator method; to change the place of integration, you only need to change the place of the call to the integrator and its parameter

- the controller has a connection with its part of the data in the store `e, remembering the path that is passed once in integrator () . This eliminates the need to know it when using data.

- the controller holds all the necessary selectors, adapters, etc.
- To track changes, you can subscribe to changes in “ your ” data.
- The reducer `s can use the call context - an instance of the module and receive from it the actionType`s belonging to the module. This eliminates the need to import a bunch of action `s and reduces the likelihood of error.
- the action`s get the context of use, because they become part of the module instance: now it’s not just trainingFinished , but readingModule.actions.trainingFinished
- action`s now exist in the module namespace, which allows you to create events for different modules with the same name
- each module can be instantiated several times, and each instance is integrated into different parts of the store’s
- action`s for different instances of a module have different actionType - you can respond to events of a particular instance
As a result, we get a black box that is able to manage its own data and has handles for communicating with external code.
At the same time, this is all the same
redux , with its unidirectional data flow, transparency and predictability.
And since this is all the same redux and all the same
reducer `s, you can build from them any structure that is required by the logic of the application domain.