What are Microservices?
Microservices architecture breaks applications into small, independent services that communicate via APIs.
Benefits
- Scalability: Scale individual services independently
- Flexibility: Use different technologies per service
- Resilience: Failure in one service doesn't crash the entire app
- Deployability: Deploy services independently
Design Principles
Single Responsibility
Each service should have a single, well-defined purpose.
javascript
// User Service - handles user management
// Order Service - handles orders
// Payment Service - handles payments
// Notification Service - handles notificationsDecentralized Data Management
Each service owns its data.
javascript
// User Service has users table
// Order Service has orders table
// Payment Service has payments tableService Communication
REST API
javascript
// Order Service calls User Service
const user = await axios.get(
'http://user-service/users/123'
);Message Queues
javascript
// RabbitMQ with amqplib
import amqp from 'amqplib';
const connection = await amqp.connect('amqp://localhost');
const channel = await connection.createChannel();
// Send message
channel.sendToQueue('orders', Buffer.from(JSON.stringify(order)));
// Consume messages
channel.consume('orders', (msg) => {
const order = JSON.parse(msg.content.toString());
processOrder(order);
});gRPC
javascript
// Faster, typed communication
const client = new userServiceClient(
'user-service:50051',
creds.createInsecure()
);
const user = await client.getUser({ id: '123' });API Gateway
An API gateway routes requests to appropriate services.
javascript
import express from 'express';
const gateway = express();
gateway.use('/users', proxy('http://user-service'));
gateway.use('/orders', proxy('http://order-service'));
gateway.use('/payments', proxy('http://payment-service'));Service Discovery
Services need to find each other dynamically.
javascript
// Using Consul
import Consul from 'consul';
const consul = new Consul();
// Register service
consul.agent.service.register({
name: 'user-service',
port: 3001,
check: {
http: 'http://localhost:3001/health',
interval: '10s'
}
});
// Discover service
const services = await consul.agent.service.list();
const userService = services['user-service'];Resilience Patterns
Circuit Breaker
javascript
import CircuitBreaker from 'opossum';
const options = {
timeout: 3000,
errorThresholdPercentage: 50,
resetTimeout: 30000
};
const breaker = new CircuitBreaker(callService, options);
breaker.fallback(() => ({ error: 'Service unavailable' }));Retry
javascript
import retry from 'async-retry';
await retry(
async () => {
const response = await fetch(url);
if (!response.ok) throw new Error('Failed');
return response.json();
},
{
retries: 3,
minTimeout: 1000,
maxTimeout: 5000
}
);Deployment
Docker
dockerfile
# user-service/Dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
CMD ["node", "server.js"]Docker Compose
yaml
version: '3.8'
services:
user-service:
build: ./user-service
ports:
- "3001:3001"
environment:
- DB_URL=mongodb://mongodb:27017
depends_on:
- mongodb
mongodb:
image: mongo:latest
ports:
- "27017:27017"Monitoring
javascript
// Health check endpoint
app.get('/health', (req, res) => {
res.json({
status: 'healthy',
uptime: process.uptime(),
timestamp: Date.now()
});
});
// Metrics
import promClient from 'prom-client';
const register = new promClient.Registry();
promClient.collectDefaultMetrics({ register });
app.get('/metrics', async (req, res) => {
res.set('Content-Type', register.contentType);
res.end(await register.metrics());
});Best Practices
- 1Keep services small: Focus on single responsibility
- 2Use async communication: Message queues for decoupling
- 3Implement circuit breakers: Prevent cascading failures
- 4Centralize logging: Aggregate logs from all services
- 5Use API versioning: Maintain backward compatibility
Conclusion
Microservices architecture with Node.js enables building scalable, maintainable systems. Start small and evolve as needed.