I recently decided to add a question to my list of interview questions to test candidates’ application design skills, which was also based on a real business problem I recently experienced:

I am currently working in the mid-stage department of technology, which has developed many tool applications for the business departments of the company, such as data analysis, A/B testing, deployment and release platform, etc. They are based on the React framework and developed in conjunction with various state management frameworks such as Redux, MOBx, or DVA. These applications are maintained and developed by different front-end teams, so it’s not hard to imagine everyone developing the same functionality or components, such as login authentication (all based on corporate OAuth), error handling, sending messages to users, and so on. Because they are scattered across different domains but are actually too interconnected to be easy to use, we plan to group them together under the same portal (site), with a common navigation bar at the top and specific applications at the bottom. Switch between different applications on the current page using the navigation menu. It gives users the feeling of using a single application. How to design this aggregation scheme?

You will understand “Micro Frontends” if you have heard of “Microservices”. Similar to back-end applications, when a front-end application becomes very large, it becomes difficult to maintain and unstable. Breaking up large applications into smaller ones allows each specialist team to focus on their own functionality, making it easier to test, deploy, and release.

However, there are many such splits. The most common one is to split a single app into multiple independent apps, which can be seamlessly switched through navigation bars and dynamic loading. Such apps can even be built using different technology stacks:

Modules on a single page can also be broken down into different microfronts developed by different teams using different technology stacks:

These are two extremes. Other scenarios include but are not limited to

  • Integrate third-party SaaS applications,
  • Integrate third-party private server applications (such as GitLab deployed internally)
  • Dealing with all of the above under the same technology stack and so on

In the same way, sometimes we need to do the reverse (postposition), such as in the first example, where there are already multiple applications and they need to be aggregated into a single application.

As you can imagine, forward top-down splitting is much easier than reverse aggregation, because at the beginning of development you can anticipate functionality, convention interfaces, unify technology stacks, extract common components, and so on; Under reverse polymerization, everything is unknown, and any difficulties may occur

In the final analysis, the aggregation problem of micro front end is actually very complicated. The complexity lies in that there are too many subdivided scenes to provide a unified solution. Therefore, the problems to be dealt with in this paper are limited to those described in the title. But even so, the ideas described in this article, and the issues you consider, may still be relevant to you in the future.

Perhaps at this point in your reading you can pause, pick up a pen and paper and think about how to solve this problem for ten or fifteen minutes. When interviewing a person, what he or she gives is not as important as how he or she approached the problem and considered the problem thoroughly. An expert and a novice may give the same answer, but the expert is the result of deliberation, while the novice may be lucky enough to find the key.

When solving this problem, my colleague recommended to me an article on micro front end by Meituan technology blog named “Building single-page Application with Micro Front End”. This article provides us with a kind of solution to the problem, which can be used as a clue to solve this kind of problem. But there is still something worth discussing in the scheme. The rest of the article will discuss these areas, focusing on two points in the article: registration mechanisms and namespaces.

In the end, pointing out these shortcomings is not meant to belittle, nor do I offer a clear, enlightened solution, but rather to offer more possibilities for discussion. It’s best to finish reading Meituan’s article and compare it with your own thinking before you start. Maybe you’ll come up with something different than us

Registration mechanism

Meituan’s needs are consistent with ours, in the words of the original text

The HR system was transformed into one domain name and one presentation style

Without additional information, I default to the view that there is no need for communication and interaction between different systems, and that each system is a separate application. But there are still logical intersections, such as user login mechanisms, global exception handling, and so on.

What Meituan did was create a new public level project called Portal. All subprojects register their information with the Portal, including routes and namespaces. All subsequent projects introduce components of this functionality. Then, as each project goes live, route (navigation bar?) Dynamically load subproject resources (scripts/styles) to dynamically load subprojects

Usually when you do reverse aggregation, in addition to creating a new container/entry level project like the one above, you also need to make changes to the original project, which can be painful because of the need for forward and backward compatibility. If it’s a project maintained by a different team, you’ll also need to push “people” to do it. Therefore, the ideal state is “non-invasive” renovation, i.e. the original project is not perceptive. This may seem difficult, but of course the next best thing is to keep the cost of change as low as possible.

In meituan’s solution, they wanted to hand over the public libraries to Portal to maintain and load without invading the transformation project, so they decided to replace the subproject’s require(‘react’) method with window.app.require(‘ React ‘) during the construction phase. The Portal’s private require method. This seems to reduce the number of concerns of subprojects while unifying the management of common libraries;

But do we really need common library management?

We can understand why they want to do common library management. On the one hand, to avoid repeated loading of resources, such as react, react-dom, Lodash class libraries should be used by each project, on the other hand, to ensure that all projects use the internal component library is the latest after release

In practice, though, subprojects are done by different teams at different times, so even basic libraries can have different versions (you might wonder: Why don’t they update? Many reasons, such as the functionality is not developed, such as the use of other libraries only compatible with this version)

So even if you want to unify the management of the class library, you still have to make intrusive changes to other projects, and there is still a lot of compatibility and testing work.

Then we can still take a step back and share some version of the component. When a subproject registers, it needs to register its dependent libraries and corresponding versions with Portal. This raises another question, how do you manage multiple versions of the same library in a single page application? For example, project A depends on react@15 and project B depends on react@16. When A user switches from A to B and then to A, does react@15 need to be reloaded? Obviously not, the class library should be cached. A better approach is not to rely on HTTP caching, but to cache already loaded libraries in memory, where you also need to resolve the same library namespace conflicts in memory.

So to sum up:

  1. If you want to unify the common class library, you need to hack the project
  2. If you insist on library reuse, you need more sophisticated common library management

Another good thing about the Meituan solution is that all the sub-applications are written using the Redux framework (because you also need to register reducers when registering with Portal? But didn’t read about uninstalling reducers when switching projects?) So they share a Redux framework. Again, in real life, you don’t have that kind of luck. When you do front-end aggregation, you need to think about how to ensure that different data flow frameworks run smoothly after dynamic loading. For example, how to switch from Redux to Mobx? Integrate all possible frameworks into Portal ahead of time? What about version control?

Everything seems to be an effort for dynamic loading. But do we really need dynamic loading?

When I was working at IQiyi years ago, I was responsible for the development of the play page (the page where the video was played), and there was a piece of logic that would automatically play the next video from the video list, or the next episode of the series, when the video on the current page was finished. However, instead of jumping to the next page, it asynchronously requests all information of the next video, including the author, comments, popularity and so on, to be updated in the current page, in order to achieve a seamless switching effect. Don’t forget to change the URL as well.

But why must it be dynamically loaded? Faster performance? Not necessarily, with so many requests and so many DOM nodes to render (there was no React and no Virtual DOM in those days), it wouldn’t necessarily be faster than a whole page rendered by a server; Better experience? I don’t think the single-page experience is bad (see Youku); Even scarier is the maintenance difficulty: imagine a new student trying to modify a dynamically loaded feature and forgetting that some logic needs to be updated.

My personal opinion is that if your aggregated projects are independent, just package them as separate apps and jump from page to page. And there is no need to do common library management, directly packaged into the final script of the project, repeat.

At this moment you may be surprised: read the article for a long time to give me such a “simple” plan? !

Young man, I understand you, every programmer is born to love “crazy cool hanging explosion” technology, in short, the more out of the door the better, the more complex the better. But when you do it at the company level, your boss, your boss, they want stability and cost. Therefore, when choosing the implementation plan, these two points should be considered first, for example

  • Plan 1: Implement 3 points of functionality, 5 points of cost, and 3 points of risk
  • Plan 2: To achieve 1 cent of the function, 1 cent of the cost, and 1 cent of the risk

The latter is actually more profitable.

If this feature may be needed later, why not look in that direction, design and implement it now? If it’s only possible, rather than a clear requirement at the moment, you’re using the time of your current project to do something you don’t need. This is called overdesign

At least in this scenario, I don’t think we need more complicated solutions

In what scenarios do you have to consider dynamic loading? For example, cross-application component (page) level sharing: Suppose you have an internal data analysis platform and an A/B test platform. On A/B testing platform, we also need to observe data, such as the effects of different experimental schemes under different indicators. If the preparation of a data analysis page has the same suspicion, directly jump to the data analysis platform and very inconvenient. Therefore, the best way is to package the components or pages of the data analysis platform independently, so that A/B platform can dynamically load the data analysis page and browse.

The namespace

A namespace needs to be specified when a subproject registers with Portal, as in this example:

 await window.app.init('namespace-kaoqin',reducers);
Copy the code

Note that both the global variable window is used and the namespace of the subproject is manually specified

I recommend you an open source book: “Single Page apps in Depth “, as the name suggests, is a book about Single page apps development. It was born before React. Back then, there were only Backbone and Angular MVC frameworks. The point is, the argument in my consent (or rather it convinced me) that namespaces are not a good practice.

At the time, the main approach to modularity was to apply a unique namespace on global variables (such as window.myapp) and then mount everything under that namespace, which caused two problems:

  • Under this mechanism, you either expose the methods and variables you want to expose globally or hide them completely. There is no case where you want to hide it and expose it to only a few subsystems for use. If you expose it completely, you have no control over other code accessing it or even relying on it
  • Since a module becomes globally accessible, you can’t be sure it won’t get “dirty” : a module that references it may accidentally modify it, making other modules that depend on it unusable. Most technical people don’t have the patience to wait for a module publisher to fix a bug or add a feature. They prefer to hack it temporarily, then think I’ll go back to it when the module implements the feature. But that never happened

So that’s why CommonJS was born and why I don’t think window.app is a good practice

On the other hand, specifying namespaces manually can be confusing. As I understand it, what they want to achieve here is to give different projects different scopes to separate them, including reducer and CSS. So it doesn’t matter what namespace is. At the extreme, even generating a bunch of random strings is OK (see CSS Modules practice)

Even if you specify namespaces manually, how do you guarantee that they won’t clash someday? Who watches the watchman ?

conclusion

Technical solutions are the product of each company and each team in a specific business scenario, there is no superior or inferior. If Meituan’s technology is unified enough, the common library version is maintained diligently enough, and the development is standardized enough, there is no problem with such a solution. I think of it more in terms of the business logic I’m facing, and obviously I’m unlucky because there are so many branch cases to deal with.

I believe that there are many mistakes in my understanding of the original text in this article. If there are friends who do this business in Meituan or who know the author of the original text, they can pass it on to each other and look forward to further correction and communication

The resources

  • Use the way of micro front – end to build class single – page application
  • Micro Frontends – a microservice approach to front-end Web development
  • Micro Frontends

This article is also published in my Zhihu column, welcome your attention