📜 ⬆️ ⬇️

As I wrote my Redux

image

Or the Hunt for the Kraken . In previous posts ( here and here ) I shared my braindump on various architectural styles, in particular Model-View-Controller and Flux .

I noted that I did not see any revolution in the face of Flux, this template is not something new. I saw in it a similarity with the 1979 Reenskaug-MVC . Also, I mentioned that I decided to remove Redux from my code (one of the implementations of Flux). It seems to me that these points need to be explained more fully. My goal was not to convince the reader that Flux should be called MVC, I also did not want to say that the redux module is bad and should be completely abandoned.
')

So how does it relate to Flux?


First you need to decide what is Flux. First of all, this is definitely an architectural style, and at that moment, not only for client web applications. Secondly, it is a set of well-defined components and terms.

There are already a dozen implementations of this concept, these are only the most popular: Facebook / Flux , Redux , Fluxxor , Reflux , Vuejs / Vuex , Mobx . There is an exotic in the form of SAM.js (State-Action-Model).

But it is important that the terms Dispatcher , Action , Store, and State (State) are tightly occupied in modern client application architectures. Just as before, the developer who came to the new system was looking for the Controller layer, now he is looking for the Vault or Dispatcher.

So how to treat this? Treat definitely good. For me, Flux is a second-level SoC (Separation of Concerns) , a division of responsibility within a specific MVC component. This is not a new idea; this is what Taligent-MVP tried to do when the Controller layer was rethought and divided into several other layers.

This is not a replacement, this is the next round, this is the evolution of architectural patterns.

image

From the note “Everything new is well forgotten old”

image

From the presentation of I.Panin “Why the domain first?”

Motivation


Responsibility is an approach that allows you to not turn the project code into a scary pasta monster, whose tentacles are randomly intertwined, building mazes of code designs ( Kraken, see previous notes ).

However, as I mentioned earlier, each concept or pattern begins with a problem for which they were invented. Facebook is a large company, it is a web-application of colossal proportions, the amount of interactivity at the highest level. That's what Flux was coined for. But that is precisely why this concept was so difficult for the community. How many companies develop similar software?

Consider how many applications you have developed really need multiple storage? By developing how many of your applications would this template really help?

For each lock you need your own key. Sometimes the keys fit several locks, sometimes we force it in and still open the door. In my head and the phrase pops up:

Too often we don’t want to listen to the voice of reason and pay a lot of attention to sellers of panacea, singing voices

V.M. Tursky (from the book of F. Brooks “The Mythical Man-Month”)

I saw a group of developers who were losing their heads from publicized standards and forgot about their main task: to realize the interests of the client.

R. Martin

Redux is loved by the community. This implementation is what the average application needed. When I moved to this stack of technologies, the first applications were written, one can say, as standard. A bunch of React-Redux-Thunk (Middleware) was used. There were both Action Creators and string constants for Action types (Actions) , there were also so-called Bound Action Creators . All guide to the official site.

But at a certain point, the question became, how does this pair of magic modules work under the hood? For a developer like me who dealt with the Documentum Foundation Classes codebase, read the source code of ExtJS 3.1 integrated into Webtop, it’s easy to figure out a couple of functions of the redux module. As with most other experienced programmers. But it is not a secret for anyone that developers of different levels work on projects. How to ensure that even the youngest could understand what is happening under this very “ hood ”? After all, the programmer must know his tool.

I was faced with the fact that not all the developers around me understood or even thought about how this magic works , how the rabbit emerges from a hat. This was probably the first point that served as a catalyst for writing your own implementation of Flux / Redux. In the end, the best way to figure out something, to rethink something, is to write it yourself. The code will be available to the whole team, everyone will be able to add or remove something, go through the debugger and understand the purpose of any line.

Go through the rest of the motivating points. Unidirectional Data Flow concept must be clean. Unidirectional data flow is what will kill a monster with tentacles. And, probably, the most uncertain point in this approach is the asynchrony of JavaScript and how it lies on the concept of a unidirectional data flow.

There are several options that people choose to solve this moment. One example is Thunk. Personally for me, middleware (intermediate computing) looks like a kind of side effect within the framework of the architectural style of Flux. Attempt to plug the hole, which does not allow to use the template in its pure form. These are like Utils, Helper or Service classes in OOP. When a developer cannot take out logic in a particular business class, he will have this magical place where he begins to put all this side (useful?) Functionality. In fact, this side effect is nothing more than a feature of our Vault. Storage is persistent , and this is what keeps us from thinking about data through the lens of monosyllabic CRUD operations.

Definitely the code should be clean. This is what professional programmers strive for in their projects. Most tools are redundant since they were created for a wide range of tasks. They were not created for your project, sometimes not even for your type of task. But everyone is chasing trends, everyone is chasing a panacea.

Thunk and bindActionCreators are a side effect for me. I choose the approach in which the second argument of the connect function, namely mapDispatchToProps , I will not need. By the way, has anyone ever used the third argument of the connect function in their projects? Can the mysterious pure flag?

The second point that served as a catalyst is the redundancy of the tools used.

By the way, one of Niklaus Wirth's favorite phrases:

The tool must match the task. If the tool does not match the task, you need to come up with a new one that would fit it, and not try to adapt an existing one.

The third point is “because I can” . Yes, yes, exactly, we are programmers too, we can also write our tools. I do not urge to rewrite, for example, React or Spring-framework. But dragging the whole Spring into a project that doesn't need it doesn't make sense. For example, I am absolutely not too lazy to spend one Saturday evening on my little implementation of Flux, which will satisfy the needs of my applications. I even enjoy it.

Bike or let it have a name, Artux


image

Redux is written in a functional style, in the question of programming paradigms, I am the happy medium, JavaScript is an amazing language that opens up a multi-paradigm curtain. I am for the very balance of the dark and light side.

But classes make it easier to describe interfaces. When you say "Storage", it is easier to perceive it as an object with a specific API method, rather than a set of functions, united by one name.

Below is just the interface of my implementation of the Store. It is very similar to the Redux variant, except that the unsubscribe function has appeared in public methods. In the redux module, it is closed in the subscribe function.

image

The Storage Designer accepts Models (Models) cards with the appropriate Action Model (Reducers / Receivers) with the corresponding models.

The model is a logical named subdivision of the Storage, which corresponds to a specific business entity, part of a domain. In my view, the Vault contains only business information, no states of graphical widgets, such as checkboxes or radio buttons.

Handlers are functions or classes that react to received commands from the outside and, based on useful information that a command carries, change the state of their Model.

Below the interface of the StoreProvider class (Storage Provider) , it is almost identical to the original and simply puts an instance of the Storage in the application context.

image

The following describes the higher order component interface (Higher-order Component / HoC) that the subscribeToStore function generates, replacing the connect function.

image

As I mentioned above, I removed the redundancy and now only the listener of the repository state is transmitted to the input. The main logic that is mixed with this component is to check the data being listened and their values ​​obtained from the next state. In the case of obtaining new values, the lifecycle methods of the component are run, which as a result will update all or part of the whole view.

And the last component of my modest implementation. Interface Manager.

image

The main purpose of the component is to send messages to the Vault. The interface provides a method that allows you to create an action object, as well as methods for scheduling actions that “ bury ” the asynchronous nature of persistent operations. Let me draw the reader’s attention to the fact that the promise is not thrown out; any reactions in the system occur only on the basis of changes in the state of a certain model.

The action is a JSON object containing the following fields:


Also, with the use of es6-objects Proxy and Reflect , a logging mechanism was added to the state transition console for easier debugging.

image

App device


At the top of the view are Pages . It can be said, the components-containers, which are the parent for the entire tree of graphic widgets.

Pages are exactly the same HoC components that subscribe to changes in the state of the Storage, forwarding the data stream down through the tree. Also, each such object has its own Interactors layer. This layer combines two types of operations: transitions between Pages (Pages) or Anchors (Anchors) and the initialization of the execution of Actions (Actions / Commands) .

The class of interaction is, in fact, a bridge, allowing to combine in one place communication with the Manager and the Router . Below is an example of an interface of this class.

image

Each model in the system corresponds to its Handler (Reducer / Receiver) , it is he who, on the basis of the received action, changes or does not change the state of the model.

Many terms I write through the line, giving two options for naming. By this, I would like to note that a similar concept can be implemented, already familiar to many, with behavioral design patterns.

My version of the handler is Reducers . I have always been annoyed by tons of constants that I had to write for the fork of action types (PENDING, SUCCESS, FAIL). First, I decided to dwell on literals right at the handlers. Secondly, I believe that the object-action is a very flexible tool that could carry all the necessary information. Thirdly, for me personally, it is somehow difficult to perceive the user's action, which is called GET_ITEMS FAIL. Agree, this is not the action that the end user wanted to perform, he performed GET_ITEMS, and it is this action that should go to the model in the repository.

image

image

Now you can display the general scheme of the application. She might look like this.

image

#DMP_END


Like any scheme, this architecture can also turn into an octopus with tentacles. The thickest place in this version is the performance. But here we are called upon to help React itself, a library focused on the implementation of the presentation layer. There are also a number of techniques, for example, component inheritance (HoC and OOP Inherits) . The task is about simple - to prevent the birth of a new monster - FSUV (Fat Stupid Ugly View) .

I must clarify that in the description, I referred to the module redux version 3.6.0. To be honest, I didn’t particularly gain in performance, page rendering takes place in the same time. But if suddenly this module disappears altogether from npm, then as you already understood, I will not be very sad.

Summing up, there is a tool for each task, I wrote my own little implementation of the tool, and for subsequent applications it may not be suitable. Perhaps it is evolving into something more universal, and maybe I will again return to the standard react-stack.

By the way, I did not specifically show the implementation of the methods, only the interfaces, let it be a motivation for the reader to go see the source codes of the libraries used, and try to create his own small implementation that would suit his project.

On this note, I will cut off my chaotic dump, the theme of which was a mess of thoughts about the architecture of Flux. I would welcome any feedback artur.basak.devingrodno@gmail.com

Links


+ Flux Standard Actions
+ Redux
+ Flux
+ I.Panin “Why the domain first?”

Source: https://habr.com/ru/post/328152/


All Articles