Design and implement microservices architecture including service boundaries, communication patterns, API gateways, service mesh, service discovery, and distributed system patterns. Use when building microservices, distributed systems, or service-oriented architectures.
Comprehensive guide to designing, implementing, and maintaining microservices architectures. Covers service decomposition, communication patterns, data management, deployment strategies, and observability for distributed systems.
Bounded Contexts:
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Order Service │ │ User Service │ │ Payment Service │
│ │ │ │ │ │
│ - Create Order │ │ - User Profile │ │ - Process Pay │
│ - Order Status │ │ - Auth │ │ - Refund │
│ - Order History │ │ - Preferences │ │ - Transactions │
└─────────────────┘ └─────────────────┘ └─────────────────┘
Decomposition Strategies:
E-commerce System:
- Product Catalog Service
- Shopping Cart Service
- Order Management Service
- Payment Service
- Inventory Service
- Shipping Service
- User Account Service
Healthcare System:
- Patient Management (Core Domain)
- Appointment Scheduling (Core Domain)
- Billing (Supporting Domain)
- Notifications (Generic Domain)
- Reporting (Generic Domain)
// order-service/src/domain/order.ts
export class OrderService {
constructor(
private orderRepository: OrderRepository,
private eventBus: EventBus,
private paymentClient: PaymentClient,
private inventoryClient: InventoryClient,
) {}
async createOrder(request: CreateOrderRequest): Promise<Order> {
// 1. Validate order
const order = Order.create(request);
// 2. Check inventory (synchronous call)
const available = await this.inventoryClient.checkAvailability(order.items);
if (!available) {
throw new InsufficientInventoryError();
}
// 3. Save order
await this.orderRepository.save(order);
// 4. Publish event (asynchronous)
await this.eventBus.publish(new OrderCreatedEvent(order));
return order;
}
}
REST API Example:
// user-service/src/api/user.controller.ts
import express from "express";
const router = express.Router();
// Get user profile
router.get("/users/:id", async (req, res) => {
try {
const user = await userService.findById(req.params.id);
res.json(user);
} catch (error) {
if (error instanceof UserNotFoundError) {
res.status(404).json({ error: "User not found" });
} else {
res.status(500).json({ error: "Internal server error" });
}
}
});
// Service-to-service call with circuit breaker
import axios from "axios";
import CircuitBreaker from "opossum";
const options = {
timeout: 3000,
errorThresholdPercentage: 50,
resetTimeout: 30000,
};
const breaker = new CircuitBreaker(async (userId: string) => {
const response = await axios.get(`http://user-service/users/${userId}`, {
timeout: 2000,
});
return response.data;
}, options);
breaker.fallback(() => ({ id: userId, name: "Unknown User" }));
gRPC Example:
// proto/user.proto
syntax = "proto3";
package user;
service UserService {
rpc GetUser (GetUserRequest) returns (UserResponse);
rpc ListUsers (ListUsersRequest) returns (stream UserResponse);
}
message GetUserRequest {
string user_id = 1;
}
message UserResponse {
string user_id = 1;
string email = 2;
string name = 3;
}
// Implementation
import * as grpc from "@grpc/grpc-js";
import * as protoLoader from "@grpc/proto-loader";
const packageDefinition = protoLoader.loadSync("proto/user.proto");
const userProto = grpc.loadPackageDefinition(packageDefinition).user;
// Server
function getUser(call, callback) {
const userId = call.request.user_id;
const user = await userService.findById(userId);
callback(null, user);
}
const server = new grpc.Server();
server.addService(userProto.UserService.service, { getUser });
server.bindAsync("0.0.0.0:50051", grpc.ServerCredentials.createInsecure());
Event-Driven with RabbitMQ:
// order-service/src/events/publisher.ts
import amqp from "amqplib";
export class EventPublisher {
private connection: amqp.Connection;
private channel: amqp.Channel;
async connect() {
this.connection = await amqp.connect("amqp://localhost");
this.channel = await this.connection.createChannel();
await this.channel.assertExchange("orders", "topic", { durable: true });
}
async publishOrderCreated(order: Order) {
const event = {
eventType: "OrderCreated",
timestamp: new Date(),
data: order,
};
this.channel.publish(
"orders",
"order.created",
Buffer.from(JSON.stringify(event)),
{ persistent: true },
);
}
}
// inventory-service/src/events/consumer.ts
export class OrderEventConsumer {
async subscribe() {
const connection = await amqp.connect("amqp://localhost");
const channel = await connection.createChannel();
await channel.assertExchange("orders", "topic", { durable: true });
const q = await channel.assertQueue("inventory-order-events", {
durable: true,
});
await channel.bindQueue(q.queue, "orders", "order.created");
channel.consume(q.queue, async (msg) => {
if (msg) {
const event = JSON.parse(msg.content.toString());
await this.handleOrderCreated(event.data);
channel.ack(msg);
}
});
}
private async handleOrderCreated(order: Order) {
// Reserve inventory
await inventoryService.reserveItems(order.items);
}
}
Kafka Event Streaming:
// event-streaming/kafka-producer.ts
import { Kafka } from "kafkajs";
const kafka = new Kafka({
clientId: "order-service",
brokers: ["kafka:9092"],
});
const producer = kafka.producer();
export async function publishEvent(topic: string, event: any) {
await producer.connect();
await producer.send({
topic,
messages: [
{
key: event.aggregateId,
value: JSON.stringify(event),
headers: {
"event-type": event.type,
"correlation-id": event.correlationId,
},
},
],
});
}
// Consumer
const consumer = kafka.consumer({ groupId: "inventory-service" });
await consumer.subscribe({ topic: "order-events", fromBeginning: false });
await consumer.run({
eachMessage: async ({ topic, partition, message }) => {
const event = JSON.parse(message.value.toString());
await eventHandler.handle(event);
},
});
// api-gateway/src/gateway.ts
import express from "express";
import httpProxy from "http-proxy-middleware";
import jwt from "jsonwebtoken";
import rateLimit from "express-rate-limit";
const app = express();
// Rate limiting
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100,
});
app.use(limiter);
// Authentication middleware
const authenticateToken = (req, res, next) => {
const token = req.headers["authorization"]?.split(" ")[1];
if (!token) return res.sendStatus(401);
jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
if (err) return res.sendStatus(403);
req.user = user;
next();
});
};
// Route to services
app.use(
"/api/users",
authenticateToken,
httpProxy.createProxyMiddleware({
target: "http://user-service:3000",
changeOrigin: true,
pathRewrite: { "^/api/users": "/users" },
}),
);
app.use(
"/api/orders",
authenticateToken,
httpProxy.createProxyMiddleware({
target: "http://order-service:3000",
changeOrigin: true,
pathRewrite: { "^/api/orders": "/orders" },
}),
);
app.use(
"/api/products",
httpProxy.createProxyMiddleware({
target: "http://product-service:3000",
changeOrigin: true,
pathRewrite: { "^/api/products": "/products" },
}),
);
// Aggregation endpoint
app.get("/api/order-details/:orderId", authenticateToken, async (req, res) => {
const orderId = req.params.orderId;
// Parallel requests to multiple services
const [order, user, products] = await Promise.all([
fetch(`http://order-service:3000/orders/${orderId}`).then((r) => r.json()),
fetch(`http://user-service:3000/users/${req.user.id}`).then((r) =>
r.json(),
),
fetch(`http://product-service:3000/products?ids=${order.itemIds}`).then(
(r) => r.json(),
),
]);
res.json({ order, user, products });
});
// service-registry/consul-client.ts
import Consul from "consul";
export class ServiceRegistry {
private consul: Consul.Consul;
constructor() {
this.consul = new Consul({
host: "consul",
port: 8500,
});
}
// Register service
async register(serviceName: string, servicePort: number) {
await this.consul.agent.service.register({
id: `${serviceName}-${process.env.HOSTNAME}`,
name: serviceName,
address: process.env.SERVICE_IP,
port: servicePort,
check: {
http: `http://${process.env.SERVICE_IP}:${servicePort}/health`,
interval: "10s",
timeout: "5s",
},
});
}
// Discover service
async discover(serviceName: string): Promise<string> {
const result = await this.consul.health.service({
service: serviceName,
passing: true,
});
if (result.length === 0) {
throw new Error(`Service ${serviceName} not found`);
}
// Simple round-robin
const service = result[Math.floor(Math.random() * result.length)];
return `http://${service.Service.Address}:${service.Service.Port}`;
}
// Deregister on shutdown
async deregister(serviceId: string) {
await this.consul.agent.service.deregister(serviceId);
}
}
# user-service-deployment.yaml
apiVersion: v1