In the ever-evolving landscape of software development, two groundbreaking architectural styles, microservices, and micro frontends, have emerged as transformative paradigms. These methodologies have been redefining the way modern applications are built and deployed. Embracing the principles of modularity, scalability, and flexibility, both microservices and micro frontends have become favored choices among development teams worldwide.
Whether you are a seasoned developer or just beginning your journey into the world of software architecture, this guide aims to equip you with a comprehensive understanding of microservices and micro frontends, and how they can elevate your application development to new heights.
What are Microservices?
Microservices is an architectural style where a monolithic application is divided into several small, loosely coupled, and independent services. All these microservices work together to form a larger system. Each service in a microservices architecture represents a specific business capability and operates as a separate unit with its own database and logic.
Microservices Tutorial
Step 1: Setting Up the Project
Create a new folder for your project and initialize a new Node.js project. Open a terminal and run the following commands:
mkdir microservices-tutorial
cd microservices-tutorial
npm init -y
Step 2: Install Dependencies
We will use Express.js and Axios for this tutorial. Install them using npm:
npm install express axios
Step 3: Create Microservices
For this tutorial, we'll create two microservices: a "users" service and an "orders" service. The "users" service will handle user-related operations, while the "orders" service will handle order-related operations.
Create two folders, "users" and "orders," inside the main project folder. Inside each folder, create an index.js
file.
Step 4: Implement Microservices
Let's start by implementing the "users" service. Open the users/index.js file and add the following code:
const express = require('express');
const app = express();
const port = 3000;
app.get('/users', (req, res) => {
const users = [
{ id: 1, name: 'John Doe' },
{ id: 2, name: 'Jane Smith' },
{ id: 3, name: 'Bob Johnson' },
];
res.json(users);
});
app.listen(port, () => {
console.log('Users service is running on port ' + port);
});
Now, let's implement the "orders" service. Open the orders/index.js
file and add the following code:
const express = require('express');
const app = express();
const port = 4000;
app.get('/orders', (req, res) => {
const orders = [
{ id: 1, product: 'Product A' },
{ id: 2, product: 'Product B' },
{ id: 3, product: 'Product C' },
];
res.json(orders);
});
app.listen(port, () => {
console.log('Orders service is running on port ' + port);
});
Step 5: Communicating Between Microservices
In this step, we will use Axios to make HTTP requests from one microservice to another. We'll modify the "users" service to fetch orders from the "orders" service.
Open the users/index.js
file again and add the following code:
const express = require('express');
const axios = require('axios');
const app = express();
const port = 3000;
const ordersServiceURL = 'http://localhost:4000';
app.get('/users', async (req, res) => {
try {
const response = await axios.get(`${ordersServiceURL}/orders`);
const orders = response.data;
const users = [
{ id: 1, name: 'John Doe', orders: orders.slice(0, 2) },
{ id: 2, name: 'Jane Smith', orders: orders.slice(1, 3) },
{ id: 3, name: 'Bob Johnson', orders: orders.slice(0, 1) },
];
res.json(users);
} catch (error) {
res.status(500).json({ error: 'Internal server error' });
}
});
app.listen(port, () => {
console.log('Users service is running on port ' + port);
});
Step 6: Run the Microservices
To run the microservices, open two separate terminals, navigate to the project folder, and run the following commands:
For the "users" service:
cd users
node index.js
For the "orders" service:
cd orders
node index.js
Step 7: Test the Microservices
Open your web browser or use a tool like Postman to test the microservices.
To test the "users" service, navigate to http://localhost:3000/users. It should return a list of users along with their associated orders.
To test the "orders" service, navigate to http://localhost:4000/orders. It should return a list of orders.
Congratulations! You've successfully created a basic microservices architecture using Node.js, Express.js, and Axios, where two microservices communicate with each other to fulfill a user request.
What are Micro Frontends?
Micro Frontends is a web development architectural pattern that extends the principles of microservices to the frontend of web applications. It involves breaking down the user interface of a web application into smaller, loosely coupled, and independently deployable frontend modules. Each module represents a distinct feature or functionality of the application and can be developed, tested, and deployed independently.
Micro Frontends Tutorial
Let's see how micro frontends work practically with a simple tutorial.
In this example, we will use Express.js to create a server that serves the individual microfrontends as static files. We also use the http-proxy-middleware library to proxy requests to the appropriate microfrontend based on the URL path. Let's get going!!!
Step 1: Set Up the Microfrontend Architecture
Create a new directory for your project and initialize a new Node.js project:
mkdir microfrontend-example
cd microfrontend-example
npm init -y
Step 2: Install Dependencies
Install the required dependencies for your microfrontend project:
npm install express
npm install express-http-proxy
Step 3: Create the Microfrontends
In this tutorial, we'll create two microfrontends: frontend1
and frontend2
.
Inside the project directory, create a frontend1
directory with an index.html
file:
<!-- frontend1/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Frontend 1</title>
</head>
<body>
<h1>Frontend 1</h1>
</body>
</html>
Similarly, create a frontend2
directory with an index.html file:
<!-- frontend2/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Frontend 2</title>
</head>
<body>
<h1>Frontend 2</h1>
</body>
</html>
Step 4: Create the Microfrontend Server
Create a new file named server.js
in the project root directory:
// server.js
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
const app = express();
// Serve frontend1
app.use('/frontend1', express.static('frontend1'));
// Serve frontend2
app.use('/frontend2', express.static('frontend2'));
// Proxy requests to the appropriate microfrontend
app.use('/microfrontend1', createProxyMiddleware({ target: 'http://localhost:3000/frontend1', changeOrigin: true }));
app.use('/microfrontend2', createProxyMiddleware({ target: 'http://localhost:3000/frontend2', changeOrigin: true }));
// Start the server
app.listen(3000, () => {
console.log('Microfrontend server started on port 3000');
});
Step 5: Start the Microfrontend Server
In the terminal, run the following command to start the microfrontend server:
node server.js
Step 6: Access the Microfrontends
Open your browser and visit http://localhost:3000/microfrontend1 to see frontend1, and http://localhost:3000/microfrontend2 to see frontend2.
Congratulations! You have successfully created a simple microfrontend setup using Node.js.
When to use Micro Frontends?
Micro frontends and microservices are architectural patterns used to build scalable and modular applications, but they address different concerns and are suitable for different scenarios.
Micro frontends are used when you have a complex web application that requires multiple teams to work independently on different parts of the user interface. By breaking the frontend into smaller, self-contained modules, each team can develop and deploy their features separately, enabling faster development cycles and easier maintenance. This approach is particularly useful in large organizations with multiple frontend teams or when dealing with legacy codebases that need to be incrementally modernized.
On the other hand, microservices are employed when designing the backend architecture of an application. With microservices, the backend is split into small, autonomous services, each responsible for a specific business capability. This promotes better scalability, fault isolation, and independent deployment of services. Microservices are a better fit for applications with complex business logic, requiring flexibility in technology choices, and the ability to scale different components independently.
Below is the chart that shows the differences between microservices and micro frontends.
In summary, use micro frontends when dealing with frontend complexity and multiple development teams, and opt for microservices when you need to create a scalable and modular backend architecture. Both patterns can complement each other in building a comprehensive, decoupled, and flexible system.
Note: I have taken the help of OpenAI in some parts of this tutorial.
Top comments (26)
Sorry, but your example is for me not a microfrontend. Your example is a simple static web server. There is no difference in your example then having two html files called microfrontend1.html and microfrontend2.html.
You can say, that a classic webserver is kind of a microfrontend if it shares stuff. But your exampele isn't even sharing stuff. Also your proxy is not needed at all, you could simply serve the staic files from the root folder and it call the folder microfrontend1 and microfrontend2 and it would work the same way as described.
The main goal of microfrontends is to have it in one view. So you usually have one shell application and multiple remotes. The shell applications host those remotes (maybe one at a time, or multiple at a time) and they are mostly loaded on runtime. You then share certain dependencies (like a common framework) and can load it from different servers. Main goal for me, is to have more losley coupled deployments, espacially when you have big teams or if you want an extension concept for your forntend. We just integrated later into our product (while having classic static file hosting for nearly 10 years). I wrote something about it here:
tech.forums.softwareag.com/t/the-p...
Can you please make a second part of this tutorial where you make the micro frontends with React - Next for example.
Sure:)
Also micro frontends with Angular. Hopefully....
Great article.
Thank you!
Great article!
Thank You!
good stuff and illustration
This does not look like micro front-end from any angle. Your
frontend1
andfrontend2
are two separate and they are not sharing anything between them nor the implementation is right. You need to read about Webpack Module Federation, Single-SPA, BFF, Open Component, Bit etc., to understand micro front-end.A great blog with good hands on examples. I liked it.
Thanks for the support!
Great article! If you want to learn more about micro-frontends recommend to go through my series of articles: thesametech.com/micro-frontend-mig...
Sure. I'll go through your articles.
Frontends are usually a little more complicated than one HTML file 🙂 Given that the frontends are React applications, do you suggest having separate node projects for each of them? Dependencies could differ between them.
Sharing code between local npm projects in the same repo can work well, though there are many wrong ways to do this. Any thoughts on this?
I suggest you read this: turbo.build/repo/docs/handbook
Amazing, thanks Derick!
Nice practical article, thanks for writing it!
Could you explain Step 4 about the Microfrontend Server in more detail?
I am not sure what
ProxyMiddleware
inserver.js
is exactly doing. What is the purpose of the middleware route/microfrontend1
if it is redirected to the same server, just to another route (/frontend1
)?So why not directly name the static
app.use
route/microfrontend1
instead of/frontend1
(app.use('/microfrontend1', express.static('frontend1'));
)?Also, the generic example of frontend1 and frontend2 doesn't really make clear for what business use case this architecture makes sense, meaning the benefit of it. I would have expected an example of the interplay between the microfrontends and microservices. E.g. frontend1 fetches products/orders and frontend2 fetches users (including products orders).