Core Principles of Micro Frontend Architecture
Micro frontends extend the principles of microservices to the frontend, aiming for modular, independently deployable components. Key principles include:
- Independence: Each micro frontend should be developed, tested, and deployed independently.
- Routing Isolation: Micro frontends should control their own routing, allowing seamless transitions between components.
- Loose Coupling: Micro frontends should minimize dependencies to reduce tight coupling between teams.
Pattern 1: Independent Routing
Independent routing enables each micro frontend to manage its own routes, providing users with a smooth navigation experience across different micro frontends. Implementing routing isolation reduces interdependencies, allowing each micro frontend to evolve independently.
Example: Using Single-SPA for Independent Routing
import { registerApplication, start } from "single-spa";
// Register micro frontends
registerApplication({
name: "navbar",
app: () => System.import("@app/navbar"),
activeWhen: ["/"]
});
registerApplication({
name: "dashboard",
app: () => System.import("@app/dashboard"),
activeWhen: ["/dashboard"]
});
// Start the single-spa application
start();
Using Single-SPA, each micro frontend manages its routing separately, allowing independent navigation between sections like `/` and `/dashboard` without tightly coupling micro frontends.
Pattern 2: Shared Libraries for Reusability
Shared libraries enable micro frontends to reuse components and utilities while maintaining consistency. For example, design systems, utilities, or APIs can be centralized, reducing redundancy and ensuring uniformity across the application.
Example: Sharing a Design System with Module Federation
// webpack.config.js (for shared design system)
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: "designSystem",
filename: "remoteEntry.js",
exposes: {
"./Button": "./src/components/Button"
},
shared: { react: { singleton: true }, "react-dom": { singleton: true } }
})
]
};
With Module Federation, each micro frontend can access shared components like `Button` from a centralized design system, promoting consistency without duplicating code across micro frontends.
Pattern 3: Communication Between Micro Frontends
Communication between micro frontends is essential for cohesive functionality, such as sharing user state or event notifications. Patterns for inter-frontend communication include custom events, shared state management, and URL parameters.
Example: Using Custom Events for Communication
// Emitting an event
const event = new CustomEvent("userLoggedIn", { detail: { userId: 123 } });
window.dispatchEvent(event);
// Listening for the event
window.addEventListener("userLoggedIn", (e) => {
console.log("User ID:", e.detail.userId);
});
Custom events allow micro frontends to communicate without direct dependencies. In this example, one micro frontend dispatches a `userLoggedIn` event, and others can listen for it to react accordingly.
Pattern 4: Centralized Authentication
For applications with user authentication, a centralized authentication micro frontend handles login and session management, reducing the need for redundant authentication logic across components.
Example: Using an Authentication Micro Frontend
The authentication micro frontend manages user login, logout, and session, emitting authentication events. Other micro frontends listen to these events to react to changes in user authentication status, avoiding duplicate login logic.
Pattern 5: Independent Deployment with Continuous Integration
To support independent deployment, each micro frontend should have its own CI/CD pipeline, allowing updates to be rolled out without affecting other parts of the application. CI/CD tooling and automation streamline deployment and minimize the risk of breaking changes.
Example: GitHub Actions for Independent Deployment
name: Deploy Micro Frontend
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Install dependencies
run: npm install
- name: Build micro frontend
run: npm run build
- name: Deploy to server
run: npm run deploy
This CI/CD pipeline deploys changes to the main branch of a specific micro frontend. Independent pipelines like this allow for isolated deployments and faster updates.
Best Practices for Micro Frontend Design Patterns
- Decouple Micro Frontends: Minimize dependencies to prevent tightly coupled components, allowing each micro frontend to evolve independently.
- Use Shared Libraries Cautiously: While shared libraries are beneficial, overusing them can lead to tight coupling. Balance shared libraries with the need for autonomy.
- Optimize for User Experience: Ensure that navigation between micro frontends is seamless, maintaining a cohesive experience for users.
Conclusion
Micro frontend design patterns, such as independent routing, shared libraries, and event-based communication, provide structured approaches for managing large-scale frontend applications. By applying these patterns effectively, teams can create scalable, modular applications that support independent development, deployment, and maintenance. As applications grow, these patterns ensure that micro frontends remain efficient, consistent, and user-friendly.