July 18, 2024 - 6 min

How Microfrontends Enhance our Digital Solutions


				Hrvoje Canadija
				

Hrvoje Canadija

Solution Architect

Microfrontend blog hero image

Q Pioneers Microfrontend Architecture for Large-Scale Projects


Q has successfully integrated microfrontend architecture into its development processes, showcasing this innovative approach in a major project for a prominent client in the Middle East. This project, serving over 12 million users, demonstrates the substantial benefits of microfrontends in modern digital solutions.


What are Microfrontends?


Microfrontends represent an architectural style where a web application is divided into smaller, more manageable fragments, each owned by different teams. Inspired by the microservices model used in backend development, microfrontends allow for independent deployment, updates, and scaling of distinct parts of an application. A prime example of this technology in action is Spotify, which utilizes microfrontends to streamline its platform’s user experience.


Advantages for Users and Developers


For End-Users:



  • Frequent Updates

    Each application segment can be updated independently, meaning faster bug fixes and feature enhancements without needing to rebuild the entire app.

  • Dynamic Experience

    This leads to a more responsive and engaging user experience.


For Developers:



  • Modular Development

    Applications are broken down into smaller, manageable pieces, simplifying development and maintenance.

  • Enhanced Collaboration

    The modularity accelerates workflows and reduces complexity, enhancing collaboration among development teams.


Tools and Processes in Microfrontend Development


Micro frontend developers typically work with a range of tools including:



  • Webpack 5 for module bundling and module federation

  • Module Federation allows applications to dynamically import remote modules (our microfrontends)

  • FE JS framework for building components

  • Docker for containerization and deployment

  • CI/CD pipelines for continuous integration and delivery


The development process begins with a detailed brief outlining the project requirements. This is followed by designing the architecture, where the application is divided into distinct, autonomous fragments. Each team develops their segment independently, ensuring it integrates seamlessly with others. Rigorous testing and iterative refinements lead to the final solution, providing a cohesive user experience despite the segmented approach.


Challenges and Solutions


While microfrontends offer significant flexibility and speed, they also introduce challenges, particularly in maintaining a cohesive design and ensuring system security. However, with robust design systems and stringent security protocols, these challenges are effectively managed. The division into smaller elements does not inherently introduce more vulnerabilities; rather, it allows for targeted security measures, enhancing overall system resilience.


Cost Implications


From a cost perspective, microfrontend solutions can be more economical in the long run. The modular approach reduces development time and resources required for maintenance and updates. While the initial setup may be slightly more complex, the efficiency gains and reduced downtime translate to cost savings for users.


Conclusion


Q’s implementation of microfrontend architecture in large-scale projects underscores the technology’s potential to revolutionize digital solutions. By enabling rapid updates, enhancing developer productivity, and maintaining system security, microfrontends represent a forward-thinking approach to web application development. As more companies recognize these benefits, microfrontends are poised to become a standard in the industry.


How Q implements Microfrontends


By decomposing our interface into distinct microfrontends, we efficiently manage key UI layout components such as the header, sidebar, footer etc.. Each microfrontend is independently developed, deployed, and maintained, ensuring a high level of flexibility and rapid iteration. Our independent services then seamlessly integrate these microfrontends using advanced lazy loading techniques; this approach not only optimizes performance but also allows us to deliver a cohesive and consistently high-quality user experience across all our digital platforms.


Microapp Pattern


Additionally, we adopt the microapp pattern, where each significant section of the application is treated as a standalone app. This approach allows teams to work on different features independently, further enhancing scalability and maintainability. By leveraging the microapp pattern, we can deploy updates more frequently and ensure that each part of the application can evolve without affecting others.


Practical Implementation


Initial Release:



  • All component implementations were housed within our core library

  • Any changes to these components caused a version bump


Evolution of Core library to v2:



  • Components’ implementation was relocated to a dedicated project (we’ll call it “Microfrontend Components”)

  • v2 exclusively exports wrappers for those components, enabling developers to integrate microfrontends without extensive project rewrites


How Does It All Come Together?



  1. React UI Library

    Our design system library

  2. React Core Library

    Fundamental library providing the core functionalities for the rest of our apps

  3. React Starter

    This acts as the starting point or boilerplate for new services. It includes the basic setup and configuration

  4. Service

    Standalone React service

  5. Microfrontends

    Self-contained frontend modules


Below is a diagram of how that looks in practice:


Microfrontends - React UI library map


Examples


Below are example component diagrams and an example of implementation of our PublicFooter microfrontend.


Component Diagram


microfrontend component diagram


Code Example


import { type FC, lazy, Suspense } from 'react';
import { ErrorBoundary } from 'react-error-boundary';


type PublicFooterProps = { /* props declaration for this component */ };

const PublicFooterLazy = lazy(
() => import('microfrontend_ref/PublicFooter')
);

export const PublicFooter: FC<PublicFooterProps> = (props) => (
<ErrorBoundary
FallbackComponent={
/*
error component to handle the logic in case the
chunk with micro frontend has failed to load
*/
}
>
<Suspense fallback={
/*
a skeleton to show while loading the chunk with micro frontend
*/
}>
<PublicFooterLazy {...props} />
/*
trigger the lazy import of the micro frontend chunk
*/
</Suspense>
</ErrorBoundary>
);

Webpack Module Federation


A powerful feature introduced in Webpack 5 that facilitates the integration and sharing of code between different applications. This approach is particularly beneficial for microfrontend architectures, where various parts of an application can be developed, deployed, and maintained independently.


Key Features and Benefits:



  • Dynamic Importing of Remote Modules

    Module Federation allows applications to dynamically import remote modules, enabling the seamless integration of our microfrontends.

  • Independent Deployment

    Different parts of an application can be updated and deployed independently without requiring a complete rebuild or redeployment of the entire application.

  • Scalability and Maintainability

    By allowing parts of the application to be independently updated and deployed, Module Federation enhances scalability and maintainability. Each microfrontend can be developed, tested, and deployed independently, allowing for better resource allocation and easier management of large-scale applications.


Webpack Config Example


import pkg from '../package.json';

const webpackRemoteConfig = (name, deps) => ({
name,
remotes: {
/*
Confirguration of microfrontend remotes
*/
},
shared: {
react: {
singleton: true,
eager: true,
requiredVersion: deps.react,
},
'react-dom': {
singleton: true,
requiredVersion: deps['react-dom'],
},
},
});

const exampleMicroFrontendPlugin =
new webpack.container.ModuleFederationPlugin(
webpackRemoteConfig(pkg.name, pkg.dependencies)
);

export default (mode) => ({
/*
Rest of your config
*/
plugins: [
exampleMicroFrontendPlugin,
],
});

Impact on the Bundle Size


After migrating to the microfrontends the bundle size of our code got smaller, because it no longer used some of the components from the core library. Unfortunately, the overall bundle size of the whole application will eventually be bigger since now this code is loaded from microfrontends and they have to have some code duplicated (such as design system, react-core, etc.) to ensure independence and separation of the microfrontends from the app that renders them.


After introducing microfrontends the whole bundle size of JavaScript files changed from 604kB to 722kB.


Solutions for the Client


This solution addresses several key challenges in modern large-scale web application development:



  1. Modularity and Scalability

    Easier to manage and scale by breaking down the application into smaller self-contained microfrontends.

  2. Performance Optimization

    Lazy loading of microfrontends ensures that only the necessary parts of the application are loaded when needed. This reduces the initial load time and improves the overall performance of the application.

  3. Maintainability

    Isolating different parts of the application into separate microfrontends simplifies maintenance. Updates or bug fixes can be made to a specific component without affecting the rest of the application. This reduces the risk of introducing new issues and makes the codebase more manageable.

  4. Flexibility and Reusability

    Microfrontends can be reused across multiple projects or different parts of the same application. This promotes code reuse and reduces duplication, leading to more efficient development processes.

  5. Technology Agnosticism

    Different microfrontends can be built using different technologies or frameworks if needed. This allows teams to choose the best tools for each specific part of the application, enabling a more versatile and adaptable development environment.

  6. Consistent User Experience

    Despite being developed independently, microfrontends can be integrated seamlessly to provide a cohesive user experience. This ensures that users perceive the application as a unified whole rather than a collection of disparate components.


Project Case in Numbers


In this project we integrated 15+ government services (similiar to the Croatian government portal “e-gradani”) using microfrontend architecture within a period of six months. Each service reuses shared components and infrastructure, significantly enhancing efficiency and scalability.


Impact on Client’s Business


Q’s implementation of microfrontends has significantly impacted our client’s business by enabling faster development cycles, improved application performance, and reduced maintenance costs. This has lead to increased productivity and efficiency of our development teams. The consistent and high-quality user experience provided by our microfrontend architecture has also enhanced customer satisfaction and engagement. Furthermore, this approach has allowed our clients to swiftly adapt to market changes and demands, implement new features with ease, and maintain a competitive edge.


Give Kudos by sharing the post!

Share:

ABOUT AUTHOR
Hrvoje Canadija

Hrvoje Canadija

Solution Architect

Hrvoje is a Solution Architect who likes his code as much as he likes his synths; small, simple, and modular. There is no request that is too complicated, a wish too demanding or coffee too strong. When he grows up, Hrvoje wants to be an F1 driver or abstract art painter.