Skip to content
Plurafy

Microservices Architecture

Microservices architecture breaks applications into small, independent, loosely coupled services. Learn to design, implement, and manage microservices-based systems.

Design and implement a microservices architecture that enables independent deployment, scaling, and team autonomy while managing complexity and communication overhead.

Monolithic: Single codebase, single deployment unit, tightly coupled Microservices: Multiple codebases, independent deployments, loosely coupled services

Single Responsibility: Each service handles one business capability Autonomy: Independent development, deployment, and scaling Communication: Synchronous (REST/gRPC) or asynchronous (events) Resilience: Handle failures gracefully Observability: Comprehensive monitoring and logging

user-service/src/server.js
const express = require('express');
const logger = require('./logger');
const routes = require('./routes');
const app = express();
app.use(express.json());
app.use(require('./middleware/logging'));
app.use(require('./middleware/errorHandler'));
app.use('/api/users', routes.users);
// Health check
app.get('/health', (req, res) => {
res.json({ status: 'healthy', service: 'user-service' });
});
app.listen(3001, () => logger.info('User service running on 3001'));
order-service/src/services/userService.js
const axios = require('axios');
class UserService {
constructor(baseUrl = process.env.USER_SERVICE_URL) {
this.baseUrl = baseUrl;
this.client = axios.create({
baseURL: baseUrl,
timeout: 5000,
});
}
async getUser(userId) {
try {
const response = await this.client.get(`/api/users/${userId}`);
return response.data;
} catch (error) {
if (error.response?.status === 404) {
throw new Error(`User ${userId} not found`);
}
throw error;
}
}
async validateUsers(userIds) {
const promises = userIds.map(id => this.getUser(id));
return Promise.all(promises);
}
}
module.exports = new UserService();
api-gateway/src/gateway.js
const express = require('express');
const httpProxy = require('express-http-proxy');
const app = express();
// Service routes
const services = {
users: process.env.USER_SERVICE_URL || 'http://localhost:3001',
orders: process.env.ORDER_SERVICE_URL || 'http://localhost:3002',
products: process.env.PRODUCT_SERVICE_URL || 'http://localhost:3003',
};
// Request logging middleware
app.use((req, res, next) => {
req.logger.info('Incoming request', {
method: req.method,
path: req.path,
query: req.query,
});
next();
});
// Route to user service
app.use('/api/users', httpProxy(services.users, {
proxyReqPathResolver: (req) => `/api/users${req.url}`,
}));
// Route to order service
app.use('/api/orders', httpProxy(services.orders, {
proxyReqPathResolver: (req) => `/api/orders${req.url}`,
}));
// Route to product service
app.use('/api/products', httpProxy(services.products, {
proxyReqPathResolver: (req) => `/api/products${req.url}`,
}));
app.listen(8000, () => console.log('API Gateway on 8000'));
shared/src/eventBus.js
const EventEmitter = require('events');
const amqp = require('amqplib');
class EventBus {
async connect() {
this.connection = await amqp.connect(process.env.RABBITMQ_URL);
this.channel = await this.connection.createChannel();
}
async publish(eventType, data) {
const exchange = 'events';
await this.channel.assertExchange(exchange, 'topic', { durable: true });
this.channel.publish(
exchange,
eventType,
Buffer.from(JSON.stringify(data))
);
}
async subscribe(eventType, handler) {
const exchange = 'events';
const queue = `${eventType}.queue`;
await this.channel.assertExchange(exchange, 'topic', { durable: true });
await this.channel.assertQueue(queue);
await this.channel.bindQueue(queue, exchange, eventType);
this.channel.consume(queue, async (message) => {
if (message) {
const data = JSON.parse(message.content.toString());
await handler(data);
this.channel.ack(message);
}
});
}
}
module.exports = new EventBus();

5. Saga Pattern for Distributed Transactions

Section titled “5. Saga Pattern for Distributed Transactions”
order-service/src/sagas/orderSaga.js
const eventBus = require('../eventBus');
class OrderSaga {
async createOrder(orderData) {
const orderId = generateId();
try {
// Step 1: Reserve inventory
await this.reserveInventory(orderId, orderData.items);
// Step 2: Process payment
await this.processPayment(orderId, orderData.amount);
// Step 3: Create order record
await this.createOrderRecord(orderId, orderData);
// Publish success event
await eventBus.publish('order.created', { orderId, status: 'confirmed' });
return orderId;
} catch (error) {
// Compensate transactions on failure
await this.compensate(orderId);
throw error;
}
}
async compensate(orderId) {
await eventBus.publish('order.cancelled', { orderId });
// Handle compensation logic
}
}
shared/src/serviceRegistry.js
const consul = require('consul');
class ServiceRegistry {
constructor() {
this.consul = new consul({
host: process.env.CONSUL_HOST || 'localhost',
port: process.env.CONSUL_PORT || 8500,
});
}
async register(serviceName, port) {
await this.consul.agent.service.register({
id: `${serviceName}-${port}`,
name: serviceName,
address: process.env.SERVICE_HOST || 'localhost',
port: port,
check: {
http: `http://localhost:${port}/health`,
interval: '10s',
timeout: '5s',
},
});
}
async discover(serviceName) {
const result = await this.consul.health.service({
service: serviceName,
passing: true,
});
return result.map(entry => ({
address: entry.Service.Address,
port: entry.Service.Port,
}));
}
}
module.exports = new ServiceRegistry();
  1. Service Boundaries

    • Clear, well-defined responsibilities
    • Data ownership and isolation
    • Minimize inter-service dependencies
  2. Communication

    • Use synchronous for real-time operations
    • Use asynchronous for eventual consistency
    • Implement timeouts and circuit breakers
  3. Deployment

    • Independent deployment pipelines
    • Version management
    • Rollback strategies
  4. Monitoring and Debugging

    • Distributed tracing
    • Centralized logging
    • Service health monitoring
  5. Resilience

    • Graceful degradation
    • Retry logic with backoff
    • Fallback mechanisms
  • E-Commerce Platforms: User, Product, Order, Payment services
  • Streaming Platforms: Content, User, Recommendation services
  • SaaS Applications: Tenant isolation with service architecture
  • Mobile Backends: Scalable API services
  • Financial Systems: Payment, Transaction, Settlement services