ποΈ Microservices Architecture
Build Scalable, Maintainable Applications
π Welcome to Microservices!
Microservices architecture is a modern approach to building applications. Instead of one large application, you build many small, independent services that work together.
Learn how companies like Netflix, Amazon, and Uber build scalable systems!
π€ What are Microservices?
Microservices is an architectural style where an application is composed of small, independent services that communicate over a network. Each service focuses on a specific business capability and can be developed, deployed, and scaled independently.
Simple Analogy:
Monolithic Application (Traditional): Like a single restaurant where one kitchen handles everything - pizza, burgers, sushi, desserts. If the kitchen has a problem, the entire restaurant stops.
Microservices: Like a food court with specialized restaurants - one for pizza, one for burgers, one for sushi. If the pizza place has issues, the others keep working!
βοΈ Monolithic vs Microservices
| Aspect |
Monolithic |
Microservices |
| Structure |
Single codebase |
Multiple independent services |
| Deployment |
Deploy entire application |
Deploy services independently |
| Scaling |
Scale entire application |
Scale individual services |
| Technology |
Single tech stack |
Different tech per service |
| Development |
One team, one codebase |
Multiple teams, multiple codebases |
| Failure Impact |
Entire app goes down |
Only affected service fails |
| Complexity |
Simple to start |
Complex infrastructure |
π― Key Principles
π―
Single Responsibility
Each service does one thing well
Focus on specific business capability
π
Autonomy
Services are independent
Own database, own deployment
π
Decentralization
No central control
Services make own decisions
π‘οΈ
Resilience
Failure isolation
Graceful degradation
ποΈ Microservices Architecture Example
E-Commerce Application
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β API Gateway β
β (Entry point for all requests) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββΌββββββββββββββββββ
β β β
βββββββββΌββββββββ ββββββββΌβββββββ βββββββββΌβββββββββ
β User Service β βProduct Svc β β Order Service β
β β β β β β
β - Register β β - Catalog β β - Create Order β
β - Login β β - Search β β - Track Order β
β - Profile β β - Inventory β β - History β
βββββββββ¬ββββββββ ββββββββ¬βββββββ βββββββββ¬βββββββββ
β β β
βββββββββΌββββββββ ββββββββΌβββββββ βββββββββΌβββββββββ
β User DB β β Product DB β β Order DB β
βββββββββββββββββ βββββββββββββββ ββββββββββββββββββ
βββββββββββββββββββ ββββββββββββββββββββ
β Payment Service β β Notification Svc β
β β β β
β - Process Pay β β - Email β
β - Refunds β β - SMS β
βββββββββββββββββββ ββββββββββββββββββββ
π§ Building Microservices
User Service Example (Node.js/Express)
// user-service/server.js
const express = require('express');
const app = express();
app.use(express.json());
// In-memory database (use real DB in production)
let users = [];
// Health check endpoint
app.get('/health', (req, res) => {
res.json({ status: 'healthy', service: 'user-service' });
});
// Register user
app.post('/api/users/register', (req, res) => {
const { name, email, password } = req.body;
// Check if user exists
if (users.find(u => u.email === email)) {
return res.status(400).json({ error: 'User already exists' });
}
const newUser = {
id: users.length + 1,
name,
email,
password, // Hash in production!
createdAt: new Date()
};
users.push(newUser);
res.status(201).json({ id: newUser.id, name, email });
});
// Get user by ID
app.get('/api/users/:id', (req, res) => {
const user = users.find(u => u.id === parseInt(req.params.id));
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
// Don't send password
const { password, ...userWithoutPassword } = user;
res.json(userWithoutPassword);
});
// Login
app.post('/api/users/login', (req, res) => {
const { email, password } = req.body;
const user = users.find(u => u.email === email && u.password === password);
if (!user) {
return res.status(401).json({ error: 'Invalid credentials' });
}
res.json({
message: 'Login successful',
userId: user.id
});
});
const PORT = process.env.PORT || 3001;
app.listen(PORT, () => {
console.log(`User Service running on port ${PORT}`);
});
Product Service Example
// product-service/server.js
const express = require('express');
const app = express();
app.use(express.json());
let products = [
{ id: 1, name: 'Laptop', price: 999, stock: 10 },
{ id: 2, name: 'Phone', price: 699, stock: 25 }
];
// Health check
app.get('/health', (req, res) => {
res.json({ status: 'healthy', service: 'product-service' });
});
// Get all products
app.get('/api/products', (req, res) => {
res.json(products);
});
// Get product by ID
app.get('/api/products/:id', (req, res) => {
const product = products.find(p => p.id === parseInt(req.params.id));
if (!product) {
return res.status(404).json({ error: 'Product not found' });
}
res.json(product);
});
// Create product
app.post('/api/products', (req, res) => {
const { name, price, stock } = req.body;
const newProduct = {
id: products.length + 1,
name,
price,
stock
};
products.push(newProduct);
res.status(201).json(newProduct);
});
// Update stock (called by order service)
app.patch('/api/products/:id/stock', (req, res) => {
const product = products.find(p => p.id === parseInt(req.params.id));
if (!product) {
return res.status(404).json({ error: 'Product not found' });
}
const { quantity } = req.body;
if (product.stock < quantity) {
return res.status(400).json({ error: 'Insufficient stock' });
}
product.stock -= quantity;
res.json(product);
});
const PORT = process.env.PORT || 3002;
app.listen(PORT, () => {
console.log(`Product Service running on port ${PORT}`);
});
Order Service (Communicates with other services)
// order-service/server.js
const express = require('express');
const axios = require('axios');
const app = express();
app.use(express.json());
let orders = [];
const USER_SERVICE = 'http://localhost:3001';
const PRODUCT_SERVICE = 'http://localhost:3002';
// Health check
app.get('/health', (req, res) => {
res.json({ status: 'healthy', service: 'order-service' });
});
// Create order
app.post('/api/orders', async (req, res) => {
try {
const { userId, productId, quantity } = req.body;
// 1. Verify user exists
const userResponse = await axios.get(`${USER_SERVICE}/api/users/${userId}`);
const user = userResponse.data;
// 2. Get product details
const productResponse = await axios.get(`${PRODUCT_SERVICE}/api/products/${productId}`);
const product = productResponse.data;
// 3. Check stock and update
await axios.patch(`${PRODUCT_SERVICE}/api/products/${productId}/stock`, {
quantity
});
// 4. Create order
const newOrder = {
id: orders.length + 1,
userId,
productId,
quantity,
totalPrice: product.price * quantity,
status: 'pending',
createdAt: new Date()
};
orders.push(newOrder);
res.status(201).json(newOrder);
} catch (error) {
console.error('Order creation failed:', error.message);
res.status(500).json({
error: 'Failed to create order',
details: error.response?.data || error.message
});
}
});
// Get user orders
app.get('/api/orders/user/:userId', (req, res) => {
const userOrders = orders.filter(o => o.userId === parseInt(req.params.userId));
res.json(userOrders);
});
// Get order by ID
app.get('/api/orders/:id', (req, res) => {
const order = orders.find(o => o.id === parseInt(req.params.id));
if (!order) {
return res.status(404).json({ error: 'Order not found' });
}
res.json(order);
});
const PORT = process.env.PORT || 3003;
app.listen(PORT, () => {
console.log(`Order Service running on port ${PORT}`);
});
πͺ API Gateway
What is an API Gateway?
An API Gateway is the single entry point for all client requests. It routes requests to appropriate microservices, handles authentication, rate limiting, and more.
Simple API Gateway Example
// api-gateway/server.js
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
const app = express();
// Route requests to appropriate services
app.use('/api/users', createProxyMiddleware({
target: 'http://localhost:3001',
changeOrigin: true
}));
app.use('/api/products', createProxyMiddleware({
target: 'http://localhost:3002',
changeOrigin: true
}));
app.use('/api/orders', createProxyMiddleware({
target: 'http://localhost:3003',
changeOrigin: true
}));
// Health check for all services
app.get('/health', async (req, res) => {
const services = [
{ name: 'user-service', url: 'http://localhost:3001/health' },
{ name: 'product-service', url: 'http://localhost:3002/health' },
{ name: 'order-service', url: 'http://localhost:3003/health' }
];
const healthChecks = await Promise.all(
services.map(async (service) => {
try {
const response = await axios.get(service.url);
return { ...service, status: 'healthy' };
} catch (error) {
return { ...service, status: 'unhealthy' };
}
})
);
res.json({ gateway: 'healthy', services: healthChecks });
});
const PORT = 3000;
app.listen(PORT, () => {
console.log(`API Gateway running on port ${PORT}`);
});
π‘ Service Communication
Synchronous (REST/HTTP)
Direct communication
- Service A calls Service B
- Waits for response
- Simple but creates coupling
Asynchronous (Message Queue)
Event-driven communication
- Services publish events
- Other services subscribe
- Loose coupling
Message Queue Example (RabbitMQ)
// Install: npm install amqplib
// Publisher (Order Service)
const amqp = require('amqplib');
async function publishOrderCreated(orderData) {
const connection = await amqp.connect('amqp://localhost');
const channel = await connection.createChannel();
const exchange = 'orders';
await channel.assertExchange(exchange, 'fanout', { durable: false });
const message = JSON.stringify(orderData);
channel.publish(exchange, '', Buffer.from(message));
console.log('Order created event published:', orderData);
setTimeout(() => {
connection.close();
}, 500);
}
// Subscriber (Notification Service)
async function subscribeToOrders() {
const connection = await amqp.connect('amqp://localhost');
const channel = await connection.createChannel();
const exchange = 'orders';
await channel.assertExchange(exchange, 'fanout', { durable: false });
const q = await channel.assertQueue('', { exclusive: true });
channel.bindQueue(q.queue, exchange, '');
console.log('Waiting for order events...');
channel.consume(q.queue, (msg) => {
const order = JSON.parse(msg.content.toString());
console.log('Received order:', order);
// Send notification
sendEmailNotification(order);
}, { noAck: true });
}
π³ Docker & Containerization
Why Docker for Microservices?
- Consistency: Same environment everywhere
- Isolation: Each service in its own container
- Portability: Run anywhere
- Scalability: Easy to scale services
Dockerfile Example
# user-service/Dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3001
CMD ["node", "server.js"]
Docker Compose (Run all services)
# docker-compose.yml
version: '3.8'
services:
user-service:
build: ./user-service
ports:
- "3001:3001"
environment:
- PORT=3001
- DB_HOST=user-db
depends_on:
- user-db
product-service:
build: ./product-service
ports:
- "3002:3002"
environment:
- PORT=3002
- DB_HOST=product-db
depends_on:
- product-db
order-service:
build: ./order-service
ports:
- "3003:3003"
environment:
- PORT=3003
- USER_SERVICE_URL=http://user-service:3001
- PRODUCT_SERVICE_URL=http://product-service:3002
api-gateway:
build: ./api-gateway
ports:
- "3000:3000"
depends_on:
- user-service
- product-service
- order-service
user-db:
image: postgres:15
environment:
POSTGRES_DB: users
POSTGRES_PASSWORD: password
product-db:
image: postgres:15
environment:
POSTGRES_DB: products
POSTGRES_PASSWORD: password
# Run all services: docker-compose up
π‘ Best Practices
Microservices Best Practices:
- Single Responsibility: One service, one purpose
- Database per Service: Each service owns its data
- API Gateway: Single entry point
- Service Discovery: Services find each other dynamically
- Circuit Breaker: Handle failures gracefully
- Monitoring & Logging: Centralized logging
- Automated Testing: Test each service independently
- CI/CD Pipeline: Automated deployment
- Documentation: Document APIs clearly
- Security: Authentication at gateway, authorization per service
β οΈ Challenges
Common Challenges:
- Complexity: More moving parts to manage
- Data Consistency: Distributed transactions are hard
- Network Latency: Services communicate over network
- Testing: Integration testing is complex
- Monitoring: Need to track multiple services
- Deployment: Coordinating multiple deployments
Solution: Use proper tools (Kubernetes, service mesh, monitoring tools)
π οΈ Essential Tools
Kubernetes
Container orchestration
- Deploy & scale services
- Load balancing
- Self-healing
Docker
Containerization platform
- Package services
- Consistent environments
- Easy deployment
RabbitMQ/Kafka
Message brokers
- Async communication
- Event streaming
- Decoupling services
Prometheus/Grafana
Monitoring & visualization
- Metrics collection
- Alerting
- Dashboards
π Our Training Course
π Related Topics