Microservices vs Monolithic Architectures: Building a Node.js Blog App Showdown ๐Ÿ›๏ธ๐Ÿš€

Smit Patel
6 min readAug 30, 2023

--

When it comes to building software applications, two architectural titans, Microservices and Monolithic, vie for dominance. In this article, weโ€™re embarking on a journey to explore the distinctions between these two approaches by creating a blog app using Node.js. Get ready for an architecture showdown that will help you weigh the pros and cons of both methods! ๐Ÿ’ป๐Ÿฐ

Monolithic vs Microservices

The Monolithic Monarch ๐Ÿ‘‘

Imagine a colossal castle that houses an entire kingdom โ€” thatโ€™s the Monolithic architecture. In the realm of software, Monolithic entails building an application as a single, tightly knit unit. All features, components, and functionalities are intertwined into a singular codebase, forming a cohesive entity.

Pros of the Monolithic Approach:

๐Ÿ† Simplicity: The development process, testing, and deployment are relatively straightforward, making it an excellent choice for smaller projects.

๐Ÿ† Consistency: Since everything resides in one place, maintaining uniform updates and consistent code becomes more manageable.

๐Ÿ† Simplified Debugging: Debugging is simplified as the entire codebase is centralized, easing the process of issue identification and resolution.

The Microservices Maverick ๐ŸŒŸ

Now envision a bustling marketplace where specialized vendors offer distinct wares โ€” thatโ€™s the Microservices architecture. In this methodology, an application fragments into loosely connected services, each with a specific function, developed, deployed, and scaled independently.

Pros of the Microservices Approach:

๐ŸŒˆ Scalability: Each service can be scaled independently, enabling precise resource allocation based on requirements.

๐ŸŒˆ Flexibility: Different services can be developed using different languages or frameworks, empowering you to choose the optimal tool for each task.

๐ŸŒˆ Isolation: Should a service encounter failure, only that particular service is impacted, sparing the remainder of the application.

Building the Node.js Blog App ๐Ÿ› ๏ธ๐Ÿ“

Letโ€™s take our exploration further by constructing a Node.js-powered blog app in both architectural styles.

Monolithic Architecture Implementation

In the monolithic scenario, our Node.js blog app encompasses the frontend, backend, and database interactions within a single codebase. This simplicity makes it an ideal option for smaller projects or situations demanding rapid development.

Project Setup:

Create a new directory for your monolithic blog app and navigate into it.

mkdir monolithic-blog-app
cd monolithic-blog-app

Install Dependencies:

Initialize a Node.js project and install the necessary packages:

npm init -y
npm install express body-parser ejs

Project Structure:

Your project structure should look like this:

- monolithic-blog-app/
- app.js
- views/
- index.ejs
- login.ejs
- blog.ejs
- data/
- posts.json
- public/
- styles/

Implementation:

  • app.js:
const express = require('express');
const bodyParser = require('body-parser');
const ejs = require('ejs');
const fs = require('fs');

const app = express();
app.set('view engine', 'ejs');
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static('public'));

const posts = JSON.parse(fs.readFileSync('./data/posts.json'));

// Routes
app.get('/', (req, res) => {
res.render('index', { posts });
});

app.get('/login', (req, res) => {
res.render('login');
});

app.post('/login', (req, res) => {
// Authentication logic
res.redirect('/');
});

app.get('/blog/:id', (req, res) => {
const postId = req.params.id;
const post = posts.find(post => post.id === postId);
if (post) {
res.render('blog', { post });
} else {
res.redirect('/');
}
});

app.listen(3000, () => {
console.log('Monolithic Blog App listening on port 3000');
});
  • views/index.ejs:
<!DOCTYPE html>
<html>
<head>
<title>Monolithic Blog App</title>
</head>
<body>
<h1>Welcome to the Blog!</h1>
<ul>
<% posts.forEach(post => { %>
<li>
<a href="/blog/<%= post.id %>"><%= post.title %></a>
</li>
<% }); %>
</ul>
</body>
</html>
  • views/login.ejs:
<!DOCTYPE html>
<html>
<head>
<title>Login</title>
</head>
<body>
<h1>Login</h1>
<form method="post" action="/login">
<input type="text" name="username" placeholder="Username">
<input type="password" name="password" placeholder="Password">
<button type="submit">Login</button>
</form>
</body>
</html>
  • views/blog.ejs:
<!DOCTYPE html>
<html>
<head>
<title><%= post.title %></title>
</head>
<body>
<h1><%= post.title %></h1>
<p><%= post.content %></p>
<a href="/">Back to Blog</a>
</body>
</html>
  • data/posts.json:
[
{
"id": "1",
"title": "First Post",
"content": "This is the content of the first post."
},
{
"id": "2",
"title": "Second Post",
"content": "This is the content of the second post."
}
]

Now you can run your monolithic blog app using node app.js and access it at http://localhost:3000.

In this example, weโ€™ve implemented a basic monolithic blog app with authentication, blog posts, and comments functionalities all in one application.

Microservices Architecture Implementation

In contrast, the microservices angle advocates for distinct services. Imagine having one service for user authentication, another for managing blog posts, and yet another for serving the frontend. The services communicate via APIs.

Project Setup:

Create a new directory for your microservices blog app and navigate into it:

mkdir microservices-blog-app
cd microservices-blog-app

Create Microservices:

Inside the microservices-blog-app directory, create three separate directories for each microservice: auth-service, blog-service, and comments-service.

Install Dependencies:

Inside each microservice directory, initialize a Node.js project and install the necessary packages:

npm init -y
npm install express body-parser ejs

Project Structure:

Your overall project structure should now look like this:

- microservices-blog-app/
- auth-service/
- app.js
- users.json
- blog-service/
- app.js
- posts.json
- comments-service/
- app.js
- comments.json

Implementation:

Letโ€™s implement the microservices.

  • auth-service/app.js:
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json());

const users = require('./users.json');

app.post('/login', (req, res) => {
const { username, password } = req.body;
const user = users.find(user => user.username === username && user.password === password);
if (user) {
res.status(200).json({ message: 'Login successful!' });
} else {
res.status(401).json({ message: 'Login failed!' });
}
});

app.listen(3001, () => {
console.log('Auth Service listening on port 3001');
});
  • auth-service/users.json:
[
{
"username": "user1",
"password": "pass1"
}
]
  • blog-service/app.js:
const express = require('express');
const app = express();
app.use(express.json());

const posts = require('./posts.json');

app.get('/posts', (req, res) => {
res.json(posts);
});

app.listen(3002, () => {
console.log('Blog Service listening on port 3002');
});
  • blog-service/posts.json:
[
{
"id": "1",
"title": "First Post",
"content": "This is the content of the first post."
},
{
"id": "2",
"title": "Second Post",
"content": "This is the content of the second post."
}
]
  • comments-service/app.js:
const express = require('express');
const app = express();
app.use(express.json());

const comments = require('./comments.json');

app.get('/comments/:postId', (req, res) => {
const postId = req.params.postId;
const postComments = comments.filter(comment => comment.postId === postId);
res.json(postComments);
});

app.listen(3003, () => {
console.log('Comments Service listening on port 3003');
});
  • comments-service/comments.json:
[
{
"postId": "1",
"comment": "Great post!"
},
{
"postId": "2",
"comment": "I enjoyed reading this."
}
]

Now you can run each microservice separately using node app.js within each directory. The auth service will be accessible at http://localhost:3001, the blog service at http://localhost:3002, and the comments service at http://localhost:3003.

Please note that this is a simplified example for demonstration purposes. In a real-world microservices architecture, you would need to consider communication between services, data consistency, error handling, and other factors.

Choosing the Right Path ๐Ÿ›ค๏ธ

The selection between monolithic and microservices hinges on factors such as project scale, team proficiency, and anticipated scalability needs.

๐Ÿ’ก Opt for Monolithic:

  • For compact to medium-sized projects with limited intricacies.
  • When rapid development takes precedence, and a swift time-to-market is vital.
  • When your team is petite and wishes to circumvent distributed system intricacies.

๐Ÿ’ก Embrace Microservices:

  • For extensive applications necessitating intricate functionalities.
  • When independent scalability is crucial and diverse technologies per service are desired.
  • When your team boasts proficiency in managing distributed systems.

Conclusion: Architectural Showdown โš”๏ธ๐Ÿฐ

In this Node.js blog app expedition, weโ€™ve witnessed the monolithic architectureโ€™s simplicity pitted against the microservices architectureโ€™s scalability and versatility. The path you opt for depends on your projectโ€™s unique requisites.

Whether you erect a towering monolith or a bustling microservices marketplace, Node.js provides the tools to mold the software realm of your imagination. Select your architecture wisely and may your coding journey be ever exciting! ๐Ÿš€

--

--

Smit Patel

Passionate about crafting efficient and scalable solutions to solve complex problems. Follow me for practical tips and deep dives into cutting-edge technologies