GoF, Pure Architecture, Perfect Code are the True Programmer’s Handbooks. But in the front-end world, many ideas from these books are not available. At least resemblance to the real world is very difficult to find. Maybe the modern front-end is ahead of time? Maybe “functional” programming and React have already proven their superiority ? In this article I want to give an example of a todo-list application that I tried to implement according to the principles and approaches described in classic books.

Framework dependency

The framework is the cornerstone of the modern front. In our company are React vs Angular vs Vue developers. I worked with each of these frameworks, and for a very long time I could not understand why I had to work with Vue from 3 years to repaint a button from red to purple? Why do I need to know how to inherit on prototypes, or the principle of the event loop, to move the same button from the left corner to the right? The answer is simple – we write library-bound applications.

Why do companies with long experience working with React? Yes, because the application is highly dependent on the features of this React itself, and in order not to break anything when repainting the button, you should smash your head over how change detection, rendering of the component tree works inside React, and how it is tied to the task of repainting the button. (I agree, these are all special cases … And in your company, are you ready to take a specialist without experience working with the framework?)

For the world of the front-end, these theses are empty phrase, and a challenge to prove the opposite. Let’s look at the official React documentation, and see an example of a simple todo-list application.

For a novice programmer (i.e. me a couple of years ago), this phrase automatically generates the output: “Here is an ideal example of a todo-list application”. But who stores the state in the component ?! For this there is a state management library (https://redux.js.org/basics/example/).

Yes, so the application has become much more understandable and simpler (no). Can we try to make the dependencies in the right direction?

Independent decision

Let’s look at the problem of todo-list not as front-end, and forget that we need to make HTML (“web is a detail”). We won’t be able to check the results with our eyes, so we’ll have to write tests (as Uncle Bob says, “TDD can be applied then”). And what is the task? What is todo-list? We are trying to write.

We get two simple classes with informative interfaces. Is that all? Tests pass. Now pick up the React.

And make sure that … Adding an item does not work. Hmm … Now it’s clear why everything needs to be written in state – so that the React component redraws after learning about the changes. But is this a reason to violate all possible principles and put logic in the view component? A little patience and courage. To solve the problem, calling forceUpdate () in an infinite loop or the Observer pattern is perfect.

I like the RxJs library, but I will not connect it, but just copy its API necessary for our task.

In my opinion, nothing too complicated. Add a test (notification of changes ).

Let’s think for a moment, does a change in a Todo element affect the state of a TodoList? Affects – the getCompletedItems / getUncompletedItems methods must return a different set of elements. Maybe it’s worth moving the toggleCompletion to the TodoList class? It’s a bad idea – with this approach, we’ll have to inflate TodoList for every feature concerning a new Todo-element (we will return to this later). But how to learn about the changes, again the Observer? To make things simpler, let the Todo-element itself communicate changes through a callback.

The full version of the program looks like this.

This seems to be what the todo-list application independent of the framework should look like. The only limitation is PL. You can implement the display for the console, or use Angular.

Perhaps the current version of the application is not complex enough to make sure that an independent approach is working and to demonstrate its strengths. Therefore, we’ll connect our imagination to simulate a more or less plausible todo-list development scenario.

Edits from the customer

The main nightmare of most projects is changing requirements. You know that edits cannot be avoided, and you know that this is normal. But how do you prepare for future changes?

Special Todo Elements

One of the key features of OOP is the ability to solve the problem through the introduction of new types. Perhaps this is the most powerful OOP technique that can single-handedly pull out a complex and cumbersome program. For example, I do not know what is required of a Todo element. It may be necessary to be able to change its name, it may be necessary to add additional attributes, it may be possible to change this element through direct access to the SpaceX server … But I’m sure that the requirements will change, and I will need different types of Todo.

It seems that to display a special type, we will need to change the view of components as well. In practice, I met (and wrote) components in which a million different conditions turn a div block from a giraffe into a machine gun. To avoid this problem, you can create a hoc component with a huge switch-case list. Or apply the Visitor pattern and dual dispatch, and let the Todo element decide for itself what type of component to draw.

The dual dispatch option is especially useful when the same type of element has different views. You can change them by substituting different TodoRenderers into the render method.

Now we are ready. Fear of new demands for “special” Todo elements has disappeared. I think the developers themselves could take the initiative, and offer a couple of features that require the introduction of new types, which are now added by writing new code and minimal change to the existing one.

Saving data to the server

What kind of application is without interacting with the server? Of course, you need to be able to save our list via HTTP – another new requirement. We are trying to solve the problem.

We do not know how the application should behave. Wait for the save to succeed and display the changes? Allow changes to be displayed, and in case of error, roll back to synchronized state? Or ignore save errors altogether? Most likely, the customer also does not know this. Therefore, changes to requirements are inevitable, but they should affect only one class responsible for saving. And on the way is the next edit.

History changes

“Need the ability to cancel / redo actions” …

A wave of new edits seems to take us by surprise. But in no case, you can not sacrifice tests. Now it is completely unclear which inheritance hierarchy is better suited, and whether inheritance is generally suitable. Therefore, nothing bad will happen if we simply supplement our dirty class (we will consider this the implementation details), sacrificing the principle of sole responsibility.

Since everything is approximately unambiguous with the change history, we will separate the management of the todo-list history into the base class.

The solution to the saving problem came by itself. Now we can not worry about which saving strategy the customer ultimately chooses. You can provide him with all 3 options to choose from, expanding the base class.

Summary

It seems that from our todo-list we’ve got a small prototype of Google Keep. 

What fundamentally differs this example from most front-end applications? We did not depend on libraries, therefore, a person who has never worked with React can understand this application. Our decisions were aimed only at obtaining the result, without distractions on the details of the framework, so the code more or less reflects the problem being solved. We managed to make it easy to add new types of Todo elements, and we are ready to change the conservation strategy.

What difficulties did we encounter? We solved the problem of updating the view using the Observer pattern without reference to the framework. As it turned out, the application of this pattern was still required to solve the main problem (even if we did not need to draw HTML). Therefore, we did not incur costs by abandoning the “services” of the change detection system built into the framework.

I would like to emphasize that writing tests was not any difficulty. Testing simple independent objects with an informative interface is a pleasure. The complexity of the code depended only on the task itself and my skills (or curvature).

What about the level of developer who would handle this task? Could Junior React Developer write such a solution? “Programming is more like a craft,” so without the practice of using OOP and patterns, I think it would be difficult. But you and your company decide what you invest in. Do you practice OOP or understand the intricacies of the next framework? I only again became convinced of the relevance of the literary works of experienced programmers, and showed how you can start using the advice of the classics at full capacity at the front-end.

Thanks for reading!