An entry that will try to dispel any doubts about the use of modular architecture in frontend applications.
There is nothing more beautiful in a developer's career than
Good architecture is easy to recognize. Ask yourself simple questions. Is what I'm doing obvious? Is it repeatable? Doesn't my work block others?
If all the answers are true, then you are working with a
What it means in practice? Together we will find the answer.
1. The problem
Let's say you have a simple feature to implement. It will be loading users and displaying loader/error based on API response.
Your code may look like the code you are about to see. If that's the case, you'll have problems that show up late, or to be more precise, as new functionality is added.
The issue will grow with the amount of functionality and the number of developers in the project.
Such an approach is vulnerable to changes in requirements and it violates the
single responsibility principlefrom
Let's refactor this code and design real architecture step by step.
If you write an entire application like this, you have to reckon with the fact that it will not be stable and friendly to changes in business requirements.
Each application should have layers with clearly defined relations. When I say layers, I mean code fragments that are logically related to each other and recur across different functionalities.
An example of such a layer could be logic for
business logic. In addition to identifying it, you still need to name it somehow, create abstractions and design relations.
In frontend applications, we can easily separate two layers -
logic and presentation.
To be honest - it's not enough. The presentation can be split into additional layers and the same for logic. You should achieve this because if not - you are not able to reuse code.
3. Design phase
Firstly, check this diagram.
These blue boxes represent features. Each functionality should take advantage of this structure and relations. These rules are crucial when the solution is growing 🐍.
This approach gives us a division of application code and generic code that can be used in other solutions. Also, you can easily publish libraries via
npmor migrate to
First, let's create some components and place them in the
uidirectory. This library will store purely presentation components, you can treat it as a
Next, we will create a
development-kitlibrary that will store generic logic to handle API requests, rendering and anything that will be useful in various applications.
Now when you create another app, you already have some base - design system and a set of useful features that speed up the work.
If you are interested about
useFetchhook you can check
Let's start with the
servicelayer that will be responsible for encapsulating the logic, performing API requests and carrying out the necessary data mapping.
Now, look at the next layer -
logic. We will use
useFetchgeneric hook to implement users fetch.
Logic done. Now it's time for the next layer -
components, which can be used only inside this application. This layer can use models from the application domain.
It's time to glue everything with
container. The layer that is supposed to bind the
Last layer will be a
module. A starting point for using the functionality. Here you can add things like
For easier recognition of individual layers, you can add a post fix to the file name -
Angularis doing that and me too.
6. A few doubts
What about the situation when module
Ashould consume something available in module
B? It will probably happen when you would like to implement an authorization mechanism in the app or something similar.
You can use the same approach as in
libsdirectory. Just create
sharedfolder and put there all reusable stuff connected with application.
7. Adding authorization
Let's imagine a situation when
UsersModuleis a protected view. Only authorized users can access it. Check the gif below to understand how it should work.
To access authorization state in different modules and be able to trigger authorization we need
AuthModuleitself. This stuff will be added to
As before we will split it into layers to achieve goals. Firstly, we add service.
Next, we need authorization logic.
It's time for
module. It groups the whole feature.
Now we need to wrap our entire application with
AuthModule. Now you can use authorization!
Last step - just consume authorization API via
useAuthContexthook and decide what should be displayed.
8. When to use?
Consider using this architecture when at least 2 or 3 of these points are truthy.
repositoryto check the full solution or play with it on
I hope you enjoyed my architectural proposal. It's mostly based on
It's not a perfect solution - as always it depends on the project. However, you have the next skill in your spellbook and you should be able to use this kind of approach.
Feel free to contact me if you have any questions/proposals. Have a nice day and good health!