Microservices Architecture
📖 Concept
Microservices decompose a monolithic application into small, independently deployable services that communicate over APIs. Each service owns its own data and business logic.
Monolith vs. Microservices:
| Aspect | Monolith | Microservices |
|---|---|---|
| Deployment | Deploy everything together | Deploy services independently |
| Scaling | Scale entire app | Scale individual services |
| Tech stack | Single language/framework | Any language per service |
| Database | Shared database | Database per service |
| Complexity | Simpler at small scale | Complex infrastructure |
| Team ownership | Shared codebase | Service ownership |
| Failure isolation | One bug can crash everything | Failures are contained |
Key microservice patterns:
- API Gateway — single entry point that routes to services
- Service Discovery — services register and find each other dynamically
- Circuit Breaker — prevent cascading failures when a service is down
- Saga — distributed transactions across multiple services
- CQRS — separate read and write models for performance
- Event Sourcing — store events instead of current state
Communication patterns:
| Pattern | Use Case | Examples |
|---|---|---|
| Synchronous (HTTP/gRPC) | Request-response, real-time | REST API, gRPC calls |
| Asynchronous (Message Queue) | Fire-and-forget, eventual consistency | RabbitMQ, Kafka, SQS |
| Event-driven | React to state changes | Redis Pub/Sub, EventBridge |
When to use microservices:
- ✅ Large teams (10+ developers) needing independent deployment
- ✅ Services with very different scaling requirements
- ✅ Need for technology diversity across components
- ❌ Small teams or early-stage startups (stick with monolith)
- ❌ If you can't invest in DevOps infrastructure
🏠 Real-world analogy: A monolith is a department store — everything under one roof. Microservices are a shopping mall — independent shops (services) connected by hallways (APIs). Each shop can renovate (deploy), hire (scale), and specialize independently. But the mall needs management (infrastructure) to work.
💻 Code Example
1// Microservices Architecture — Patterns23// === API Gateway Pattern ===4const express = require("express");5const { createProxyMiddleware } = require("http-proxy-middleware");67function createGateway() {8 const app = express();910 // Route to service based on path11 const services = {12 "/api/users": "http://user-service:3001",13 "/api/orders": "http://order-service:3002",14 "/api/products": "http://product-service:3003",15 "/api/payments": "http://payment-service:3004",16 };1718 for (const [path, target] of Object.entries(services)) {19 app.use(path, createProxyMiddleware({20 target,21 changeOrigin: true,22 pathRewrite: { [`^${path}`]: "" },23 onError: (err, req, res) => {24 res.status(503).json({ error: `Service unavailable: ${path}` });25 },26 }));27 }2829 return app;30}3132// === Circuit Breaker Pattern ===33class CircuitBreaker {34 constructor(options = {}) {35 this.failureThreshold = options.failureThreshold || 5;36 this.resetTimeout = options.resetTimeout || 30000;37 this.state = "CLOSED"; // CLOSED, OPEN, HALF_OPEN38 this.failureCount = 0;39 this.lastFailureTime = null;40 this.successCount = 0;41 }4243 async execute(fn) {44 if (this.state === "OPEN") {45 if (Date.now() - this.lastFailureTime >= this.resetTimeout) {46 this.state = "HALF_OPEN";47 } else {48 throw new Error("Circuit breaker is OPEN — service unavailable");49 }50 }5152 try {53 const result = await fn();5455 if (this.state === "HALF_OPEN") {56 this.successCount++;57 if (this.successCount >= 3) {58 this.reset();59 }60 }6162 return result;63 } catch (err) {64 this.failureCount++;65 this.lastFailureTime = Date.now();6667 if (this.failureCount >= this.failureThreshold) {68 this.state = "OPEN";69 console.error(`Circuit OPENED after ${this.failureCount} failures`);70 }7172 throw err;73 }74 }7576 reset() {77 this.state = "CLOSED";78 this.failureCount = 0;79 this.successCount = 0;80 console.log("Circuit CLOSED — service recovered");81 }82}8384// Usage85const orderServiceBreaker = new CircuitBreaker({ failureThreshold: 3, resetTimeout: 10000 });8687async function getOrders(userId) {88 return orderServiceBreaker.execute(async () => {89 const response = await fetch(`http://order-service:3002/orders?userId=${userId}`);90 if (!response.ok) throw new Error(`Order service: ${response.status}`);91 return response.json();92 });93}9495// === Saga Pattern (Orchestration) ===96class OrderSaga {97 async execute(orderData) {98 const steps = [];99100 try {101 // Step 1: Reserve inventory102 const reservation = await this.reserveInventory(orderData);103 steps.push({ action: "reserve", data: reservation });104105 // Step 2: Process payment106 const payment = await this.processPayment(orderData);107 steps.push({ action: "payment", data: payment });108109 // Step 3: Create order110 const order = await this.createOrder(orderData, reservation, payment);111 steps.push({ action: "order", data: order });112113 // Step 4: Send notification114 await this.sendNotification(order);115116 return order;117 } catch (err) {118 // Compensating transactions (rollback in reverse order)119 console.error("Saga failed, rolling back:", err.message);120121 for (const step of steps.reverse()) {122 try {123 await this.compensate(step);124 } catch (compensateErr) {125 console.error(`Compensation failed for ${step.action}:`, compensateErr);126 // In production: send to dead letter queue for manual resolution127 }128 }129130 throw err;131 }132 }133134 async compensate(step) {135 switch (step.action) {136 case "reserve": return this.releaseInventory(step.data);137 case "payment": return this.refundPayment(step.data);138 case "order": return this.cancelOrder(step.data);139 }140 }141142 async reserveInventory(data) { return { reservationId: "res_123" }; }143 async processPayment(data) { return { paymentId: "pay_123" }; }144 async createOrder(data, res, pay) { return { orderId: "ord_123" }; }145 async sendNotification(order) { console.log("Notification sent"); }146 async releaseInventory(data) { console.log("Inventory released"); }147 async refundPayment(data) { console.log("Payment refunded"); }148 async cancelOrder(data) { console.log("Order cancelled"); }149}150151module.exports = { createGateway, CircuitBreaker, OrderSaga };
🏋️ Practice Exercise
Exercises:
- Design a microservice architecture for an e-commerce platform — draw the service boundaries and communication patterns
- Implement a Circuit Breaker class with CLOSED, OPEN, and HALF_OPEN states
- Build an API Gateway that routes requests to different backend services
- Implement the Saga pattern for a multi-step order process with compensating transactions
- Set up inter-service communication using both REST (synchronous) and a message queue (async)
- Implement service health checking — the gateway routes traffic only to healthy services
⚠️ Common Mistakes
Starting with microservices — most applications should start as a monolith and split into services when team size and complexity demand it
Creating too many services — 'nano-services' add overhead without benefit; each service should represent a significant business capability
Sharing databases between services — this creates tight coupling; each service should own its data and expose it via APIs
Not implementing circuit breakers — when a downstream service fails, the calling service also fails; circuit breakers prevent cascading failures
Using synchronous communication for everything — fire-and-forget operations (emails, notifications) should be asynchronous via message queues
💼 Interview Questions
🎤 Mock Interview
Mock interview is powered by AI for Microservices Architecture. Login to unlock this feature.