DEV Community

Jonathan de Araújo Silva
Jonathan de Araújo Silva

Posted on

The approaches and challenges of Micro Frontends. A theoretical introduction

Today I started and completed this Introduction to Micro frontends course, on Pluralsight, and decided to bring some theoretical thoughts on the topic here, to help me fix the information in my head and have written down some highlights of the course.

The idea behind Micro Services and what it has to do with Frontend

My understanding is that, for years, software engineering teams suffered from the problems that large codebases have brought. The way that software naturally evolves, makes it harder to support, maintain and even worse to improve or add new features to it, and it has everything to do with scalability.

The need for a software to scale may come for different reasons, and scale might mean different things: It can be because of a large user base, requiring a system to scale in both availability and reliability; It could be due to a large feature count, requiring the software scale in its intrinsic complexity and, therefore, making a software team to grow up in number. For both reasons, one of the viable solutions adopted by the industry is to break the system into small, independent, resilient and connected micro-systems, making the effort to make changes or scale the whole system more strategic, in the sense that one could scale up or down specific parts of the system on demand; faster in the sense that a change in a small section of the system would not require a full deploy; And healthier for the teams since, ideally, this breakdown also means a breakdown on the team into small squads responsible for one or some of these micro services.

Small, independent, autonomous and resilient: Enter Micro Services.

It is not new anymore, everybody has heard about it in the last years. The idea is to highlight some of its fundamentals so that we can understand how these characteristics can be interesting also on user facing systems, whether its a web application or a mobile native application.

Small

The idea of size may vary here, but it is more tied to responsibility than size on lines of code or number of classes or files. Each micro service must be responsible for a single part of the domain of business in which the system acts. Do the system requires user authentication? What I have seen is a micro service to take care of this part, and act as the source of truth regarding user's identity and permissions. Does the system requires online payment? It might be interesting to have a service dedicated to integrate with the payment gateways and abstract all the back and forth related to authorizing online transactions.

In this context, what is important to understand is that this micro service must bring value to the table, from the business or from the engineering point of view, and still be small enough that it can be handled by a small group of people.

Independent and autonomous

Imagine the payments micro service mentioned above. From the point of view of the clients of this service, which can be a bunch of other services in the same system or other external systems, it does not matter what technologies are involved in the implementation for this service. Does it uses database x or y? Is it using Java Spring, Kotlin Ktor or Python Flask? Does it uses an asynchronous queue system and is deployed on Amazon AWS? None of these things are important if the micro service exposes a good interface that allows other systems to interact with it.

This characteristic makes it easier to choose whatever tech is more appropriate for the job without a restriction imposed by a monolithic software. The team responsible for the service is theoretically totally autonomous in the choices of frameworks and tools, being restricted only by business or company related constraints.

The autonomy extends also to the way the service can scale up and down. A scale up on a payment service would not impact the catalog service of a e-commerce software. It would be done without any wired consequences on the other services, only the ones related to the demand these other services impose on the payments service. From the engineering point of view, it means that a service can be independently scaled on demand without having to unnecessarily scale up other sections of the system. Cost wise, it is a good decision; People wise, it is also a good decision, since it would not involve a lot of people other than the team responsible for that service.

Resilient

The increased resilience comes from the fact that a section of the software can be setup in a way that it can handle high stress and a load of demand independently. Ideally it also includes a fallback strategy to keep the whole system up, even partially, when a specific section of the system is not feeling well.

Alright, but what about frontend?

As soon as we get a product mature enough, we shall notice the same problems backend folks have: Hard to maintain code, with too much dependencies that prevents local modifications and, due to the actual size of the codebase, hard to renew the tech chosen, change the framework or whatever, without a huge amount of work to fix stuff. All of sudden, add a new feature means touching every layer of the software and a huge git diff as a result in the PR.

Therefore, it is clear that frontend could also use some of the benefits of breaking down the codebase into small pieces that put together make the system complete. So, how do we achieve the same benefits of Micro services using frontend technologies?

The course I referred to in the introduction of this article groups the different approaches to tackle the breakdown of a web application into two different environments: shared or separated Runtime

Separate Runtime strategies

Let me start with this approach, as it is not my favorite and I will try to explain why.

The first approach can be achieved by building each one of the pages of an application as separate applications, something called "micro apps". So, if you have an e-commerce system you would have an app for the products catalog, another one for the products detail and another one for the cart and checkout. All of them relying on the user as the integration point. Let's try to analyse how it behaves related to the desired characteristics we want from micro services and also related to UI/UX point of view. Spoiler alert - All of the suggestions represent feasible ways of achieving the desired Micro services characteristics, they have some downsides related to the technologies involved on each one of them.
Let's see:

  1. Size
    Separating a web app into different pages may not be what we really want here, but it sure is a way to reduce the size of a codebase and make it easy to maintain over time.

  2. Independent and autonomous
    As a single piece of the application, it can be built in a way that it is completely independent of the other sections of the page and still have an interface so that the other pages can send information when loading it - via query parameters, for instance.
    Another point is that being independent parts, each page could use whatever web technology to build it. It does not need to be the same used on the other pages and could even be a server rendered based page using one of these template engines.
    The autonomy is achievable since it can be re-deployed without needing to re-deploy all other pages of the system. As long as the interface is maintained, the other pages' maintainers don't need to worry about the lifecycle of the pages being redeployed.

  3. Resilient
    Just like micro services, a page could be setup in a way that it can handle more traffic than other pages on the system based on analysis on the access data. An e-commerce probably get more accesses on a catalog page than on the checkout page, since just a fraction of the people buys the products. This approach can still be extended to specific sub-sections of an application: Are we close to a holiday and the company wants to create a new category of products that might get a lot of traffic? Deploy it as a separate page and just scale it up instead of the whole system.

  4. UI/UX point of view
    The problem of this approach is having the user as the integration point. If a user is in the catalog page and wants to take a look at its cart, would be necessary to navigate away to check it out. It means that a full page hard-reload will happen and it might be frustrating for the user to be jumping around.

The second approach of this group of separated runtime uses iframes, which are basically a mechanism to allow embed web pages into a main container page, working on completely separate contexts.

In the context of Micro frontends, one product could use iframes to separate the system into page sections, like a page catalog, a search box and an advertising section. Just like the micro apps approach, it scores on independence, autonomy and resilience - this last one with a slight difference, since it would still require a full load and traffic on a server but a faulty section might make the page break if not well designed for such cases. The problem with this approach lies in the limitations this technology has: It loads multiple runtimes at the same time, which can lead to performance issues. Also, there are some security vulnerabilities associated to the use of iframes and it seems that some browsers don't know to properly handle them, making their integration with the host page be bad.

This is definitely my least favorite approach to build Micro frontends applications.

Single Runtime strategies.

This group contains the two approaches that I like most: Framework components or Web Components based strategies.

Framework components

Let's tart with the Framework based Micro frontends. Here I am talking about frameworks like Reactjs, Angular or whatever else Components based framework. We are used to create components that are basically visual representations of data, but rely on a shared inner layer (Services or domain, call it as you please) to connect with a backend. Every data-wired component on a regular application touches at least one service of this layer to interact with the underlying backend.

_What if we break our e-commerce pages into totally independent sections based on React/Angular components that DO NOT share a common inner layer of services? _

Each one of them would be maintained by different teams, hosted on different git repositories and would only be glued together in the base application. We would use all the benefits of the chosen framework combined with this Micro services mindset.
But I want to reuse my services layer, how can I do it? Just share whatever is necessary as regular npm packages, that could be imported on a Micro frontend project and user as we all do now. Even our Micro frontends would use this strategy to be loaded into the main host app.

Isn't it nice? I THINK SO! But we have some downsides to it. First, we would need to use a common framework on all our Micro frontends, hurting a bit our Independence and Autonomy rule. Another thing that kinda of breaks this rule is the fact that modifying versions of dependencies might be a problem since all of these pieces share the runtime. So, two versions of React might be a problem if not properly handled. Second, we would need to work out the distribution strategy for the shared and main packages of our mini-apps, creating a bit of overhead.

Web Components for the rescue

We can overcome the issues found on the Framework components strategy using something called Web Components. In a really simplistic overview it is a modern web technology, totally tech agnostic - you can use whatever you want to write them - that let's you create reusable and shareable custom HTML elements and load them into existing web pages. It has some specific mechanisms to build and load these custom HTML elements, but the important part is that they can be completely self-contained (Hence, Independent and Autonomous) and work on most of the modern browsers.

Using this strategy, a product can achieve the Micro services best characteristics previously stated and not deal with the limitations imposed by the other strategies that I presented today. Seems like a good choice!.

Conclusion

Alright, that is what I have for today. I hope that I could show some good insights about these approaches; There might be other ways of achieving the goals we set here, but I tried to focus on these based on the introductory course I finished.

As I said before, these are theoretical ideas that I plan to put in practice soon and probably by this time I will have a better feeling of the problems and challenges that I probably still don't know exist.

thank you for reading all of this, and let me know what do you think about my thoughts. Have a nice day, and TAKE CARE!

Top comments (0)