Micro-frontends using Vue.js, React.js, and Hypernova
If you are not familiar with Hypernova you can read this article.
Evolution of Software Architecture
In the early days of software development frontend and backend code was maintained by the same team, using the same runtime environment and deployment process, nowadays we call these monolith applications.
As systems become more complex we started splitting the code in two different repos maintained by two teams with specialized skills: frontend and backend, the backend team can focus on building a resilient and high available system while the frontend team can focus on the browser compatibility, UI design, and UX. But that wasn’t enough, as complexity grew we started splitting the business logic on the backend in more maintainable services, it was how the micro-services came up.
Micro-services promote team ownership allowing teams to focus on a specific business domain, choosing the best tech stack to solve a problem regardless of how the other services are built, micro-frontends extends the concept of microservices to the front-end world.
What are Micro-frontends?
The idea behind Micro-frontends is to think about a website or web app as a composition of features owned by independent teams. Each team has a specific area of business or mission it cares about and specializes in. A team is cross-functional and develops features end-to-end, from the database to the user interface.
Design Principles
- Team ownership: An entire team is responsible to develop a set of features that belong to a specific business domain, including developing, testing and deploy process.
- Tech agnostic: The team can choose any framework like Vue.js or React.js regardless of what’s using the other ones.
- Favor native browser features over custom APIs: Use Browser Events for communication instead of building a global PubSub system. If you really have to build a cross-team API, try keeping it as simple as possible.
- Resilient site: Your feature should be useful, even if JavaScript failed or hasn’t executed yet. Use Universal Rendering and Progressive Enhancement to improve perceived performance.
We’re gonna cover those principles in the example application 🎉
What are we gonna build?
We have two micro-frontends, the first one is responsible for rendering a Navbar using React.js and the second one is responsible for rendering a Product List using Vue.js.
Both are Isomorphic Web Applications so the views are server-side rendered and client-side hydrated in order to make them client-side dynamic.
The code for the code example is available in this repo.
The project contains three folders:
- hypernova-server-vue: contains the Product List component built using Vue.js.
- hypernova-server-react: contains the Navbar component built using React.js
- hypernova-aggregator: requests views to the hypernova servers providing data and composes all the views inside an HTML Document.
CSS files are distributed by the aggregator because it’s usually responsible for the branding and look and feel.
Hypernova Vue
We have inside hypernova-server-vue
an entry point for our hypernova server on src/index.js
You can run it using yarn dev
or npm run dev
index.js
The getComponent
provides the right component decorating it with the renderVue
function of hypernova-vue
ProductList.vue
It’s a simple vue component that receives props like title
, items
but as you can see it has a method called select
that creates a CustomEvent and dispatches it using the document reference. The mechanism to communicate micro-frontends is pretty similar to the micro-services do except micro-frontends use the DOM API as pub/sub.
client.js
The client.js
script is our entry point for client-side hydration.
Hypernova React
We have inside hypernova-server-react
a similar set-up as we have on hypernova-server-vue
You can run it using yarn dev
or npm run dev
index.js
As you can see the entry point is pretty similar to the one but it uses renderReact
function of hypernova-react
instead.
Header.jsx
The Header.jsx
component subscribes to the itemSelected
event in the componentDidMount
method, therefore, every time a itemSelected
event is dispatched it will increment the itemsSelected
variable on the state and store the last item selected.
client.js
Hypernova Aggregator
The Aggregator is a basic express application that uses axios to request the views to the hypernova servers and putting the result HTML in a template using interpolation, as well as, adding the necessary client-side scripts from each micro-frontend.
You can run it using yarn start
or npm run start
index.js
The final step is open http://localhost:8080/ in your browser and you will see the page rendered, you can confirm that was server-side rendered taking a look in the page source.
Final Thoughts
As you already noticed using hypernova we can achieve the main design principles for micro-frontends, we promote Team ownership and Tech agnostic principles using a hypernova server for each micro-frontend in order to render components related to a certain business domain or feature. Also, we promote the Resilient Site principle having an Isomorphic Web Application that will be available even if the javascript scripts fail and finally we promote Native Browser Features using DOM event to communicate our micro-frontends 🙌
I hope you enjoyed this article, Thanks for reading!!