Server Side Composition, as the name suggests, is a pattern that assembles Fragments on the server side.
Here are some of the architectures which I know.
Layout Server + Fragments Server is a simple Server Side Composition.
The Fragments Server here means the server that returns the Fragments by each Micro Frontends team, and the Layout Server is the server side application such as Rails, PHP, Node.js, etc. that assembles the Fragments and returns the final HTML, CSS, and JS.
First, let's consider the Node.js Layout Server + Fragments Server.
For example, if there are two Fragment servers, one exposing React components and the other exposing Vue components, the Layout Server can parse and assemble them.
So, what if your existing application is written in a language other than Node.js? In fact, I think that's the pattern in many cases.
In this case, it's difficult to handle framework-specific Fragments, so we'll take an HTML string and assemble the Fragments.
Both of these patterns seem to work well at first glance.
However, let's consider the case where we want to pass the data from the server to the component and then SSR it. In this case, the Fragments server itself needs to have an interface that can receive data and return a response. It is necessary to have some common understanding within the organization. Also, if you want to return HTML string, you need to implement it in such a way that the component is rendered and converted to a string after receiving the data. Versioning should be done well, too. Isn't it getting tedious to do like this?
The Layout Server + Fragments Server pattern is straightforward, but the difficulty increases when you try to deal with a variety of requirements.
The Layout Server + Fragment Gateway pattern is similar to the Gateway Aggregation pattern introduced in previous chapter. The role of the Fragment Gateway here is similar to that of the API Gateway in Gateway Aggregation. It takes care of the access to multiple Fragments, separates the responsibilities from the Layout Server, and unifies the interfaces from Frontend.
Let's look at a example. The following figure shows an architecture using Hypernova for Micro Frontends.
Here, the Layout Server is made of Rails and the Fragment Gateway is made of Node.
1) Layout Server creates data for the server, 2) requests it to Fragment Gateway, 3) then Fragment Gateway pours the data into the component, and finally 4) HTML with the data is returned1.
The role of the Fragment Gateway is to abstract Fragments as an API from the perspective of the Layout Server, allowing it to become the gateway for handling a wide variety of Fragments as SSR-enabled components. In addition, no matter what framework is used on the Fragments side, the Fragment Gateway can handle it without changing existing server architecture2.
The above is the basic architecture of Layout Server + Fragment Gateway. There are several frameworks that can do Server Side Composition with this pattern, including OpenComponents, Podium, and PuzzleJs.
Here, I would like to introduce a library called Tailor.
Tailor is not a Layout Server + Fragment Gateway pattern, but rather a more sophisticated Layout Server, with some unique features in terms of assembling Fragments.
In Tailor, you can resolve Fragments by doing the following
<script type="fragment" src="http://assets.domain.com"></script>
<fragment src="http://content.domain.com" primary></fragment>
<fragment src="http://footer.domain.com" async></fragment>
Each Fragment is requested asynchronously and can be prioritized as
Fragments are delivered as a stream, so you don't have to wait for all Fragments to be completed, which speeds up Time to First Byte.
Fragments to be SSR need Rehydration, which means we need JS/CSS assets.
Tailor will respond with the asset information in the Link Header. The nice thing about this is that you can tell Tailor which assets to load. For example, consider the following response for an asset with a hash
In this case, even if you generate different hashes for different client versions, you can still read the assets that are responded to, making it easier to use a caching strategy.
Using a library like Tailor that focuses on Layout Server functionality reduces the number of considerations on the part of the Micro Frontends consumer, while optimizing for TTFB and asset management.
The Ara Framework is based on Hypernova, and provides a CLI and a set of modules for building Micro Frontends. Among them, the architecture of Server Side Composition is unique.
The whole picture is as follows.
For details, please refer to Doc, but here I will just give an overview.
First of all, Author's Medium shows that the Ara Framework is designed with the Strangler Pattern in mind.
For example, imagine a monolithic application built in Rails or Laravel and gradually refactor it into an architecture suitable for Micro Frontends.
The following is a description based on this assumption.
The Nova Proxy is a reverse proxy that sits between the existing application, which takes request from the browser, and render HTML.
The PHP server communicates with the data layer, and when generating the HTML, placeholders are embedded in advance and returned to the Nova Proxy.
The Nova Proxy parses the HTML it receives and requests the data embedded in the placeholders as a payload to the Nova Cluster. Its job is then to replace the placeholders with the returned Fragments. This layer is also responsible for handling fallbacks and timeouts, for example.
Nova Cluster is the Fragment Gateway in this chapter.
Nova Cluster receives data from Nova Proxy to multiple Fragments at once. Based on the requests it receives, Nova Cluster queries each Fragment, generate HTML, and returns it to Nova Proxy.
With this architecture, existing servers can reduce their awareness of Micro Frontends and focus on building the data. It may also be possible to break down the responsibilities, gradually moving the existing rendering logic to the Nova Proxy, and decoupling the Backend layer as an API.
One of the things that Server Side Composition can accomplish is SSR. Another advantage is that it can be implemented in Stream. In addition, by providing a Fragment Gateway, for example, the client no longer has to make multiple requests to Fragments. This kind of performance and SEO requirements may be mandatory for some applications, in which case Server Side Composition will need to be implemented.
Also, if you have an existing monolithic server side application that you want to decompose into microservices, you need to make the Frontend and Backend loosely coupled. As shown in the example of Ara Framework, Server Side Composition can flexibly handle the case where a legacy monolithic application is gradually refactored.
One of the disadvantages, as you may have noticed, is the increase in complexity. You need to consider an architecture that is not only in Frontend, and since there will be server resources, you will also need to design for availability and scalability. It is always important to be aware of how these will ultimately improve the development efficiency of the organization.
This is also true for Micro Frontends in general, but there is no de facto technology yet. Since the existing systems and the sense of challenges differ from organization to organization, I believe that the situation is such that each company implements the best, and the companies that have the strength makes their software Open Source (therefore, the architecture of the libraries introduced in this chapter varies). It is necessary to understand the design concept of the library or framework, determine whether it fits your company's challenges, and if not, you need to implement it by yourself.
In this chapter, I introduced Server Side Composition and the architecture of some libraries. I believe that with Server Side Composition, we can adopt a flexible architecture that will be helpful while thinking real-world complex challenges.
I'm sure this will be discussed in the case of Microservices as well, but I think the organization needs to have a common understanding of whether to align the frameworks. First of all, it can be seen that it is more natural to have Framework Agnostic as an interface. The fact that each team can select technologies independently is also close to the original idea of Micro Frontends. However, in reality, there is more to think about with different frameworks (complexity of coupling layers, bundle size, reinventing the wheel within the company), and there is also the benefit of being able to share information in a "Guild" style. It is my opinion that you need to think about the use case on the user side and consider which is more suitable. ↩