Microservices Architecture
A comprehensive guide to microservices architecture covering service decomposition, API gateways, service mesh, domain-driven design, communication patterns, and practical implementation strategies for building scalable distributed systems.
Microservices architecture is an approach to building software systems as a collection of small, independently deployable services. Each service runs in its own process, communicates via lightweight protocols (typically HTTP/REST or messaging), and is organized around business capabilities.
This architectural style enables organizations to scale development teams, deploy independently, and adopt different technologies for different services. However, it also introduces significant complexity in areas like service discovery, data consistency, and distributed tracing.
Decomposing a monolithic application into microservices is one of the most challenging aspects of adopting this architecture. The goal is to create services that are cohesive, loosely coupled, and aligned with business capabilities.
Decompose by Business Capability
Decompose by Data Domain
Decompose by Subdomain
Strangler Fig Pattern
Domain-Driven Design provides a framework for modeling complex business domains and finding the right service boundaries. It's essential for successful microservices architecture.
Bounded Contexts
Ubiquitous Language
Aggregates
Domain Events
// Domain Aggregates Example (Order Management)
public class Order extends AggregateRoot {
private OrderId id;
private CustomerId customerId;
private List<OrderItem> items;
private OrderStatus status;
private Money totalAmount;
public void addItem(Product product, int quantity) {
// Business rule: validate stock
// Domain event: OrderItemAdded
}
public void complete() {
if (items.isEmpty()) {
throw new DomainException("Cannot complete empty order");
}
this.status = OrderStatus.COMPLETED;
registerEvent(new OrderCompletedEvent(this));
}
}
// Bounded Context: Order Management
// Bounded Context: Inventory Management
// Bounded Context: Customer Management
The API Gateway is a single entry point for clients that routes requests to appropriate microservices. It handles cross-cutting concerns like authentication, rate limiting, logging, and request aggregation.
Request Routing
Authentication & Authorization
Request Aggregation
Rate Limiting & Circuit Breaking
# Kong API Gateway Configuration
_format_version: "3.0"
services:
- name: order-service
url: http://order-service:8080
routes:
- name: order-route
paths:
- /api/v1/orders
plugins:
- name: jwt
- name: rate-limiting
config:
minute: 100
hour: 1000
- name: customer-service
url: http://customer-service:8080
routes:
- name: customer-route
paths:
- /api/v1/customers
plugins:
- name: jwt
# Istio VirtualService (Kubernetes)
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: api-gateway
spec:
hosts:
- api.example.com
gateways:
- api-gateway
http:
- match:
- uri:
prefix: /api/v1/orders
route:
- destination:
host: order-service
port:
number: 8080
- match:
- uri:
prefix: /api/v1/customers
route:
- destination:
host: customer-service
port:
number: 8080
A service mesh is a dedicated infrastructure layer for handling service-to-service communication. It provides advanced networking capabilities including traffic management, security (mTLS), observability, and resilience patterns without requiring changes to application code.
mTLS Security
Traffic Management
Observability
Policy Enforcement
# Istio Service Mesh Configuration
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: order-service
spec:
host: order-service
trafficPolicy:
connectionPool:
tcp:
maxConnections: 100
loadBalancer:
simple: ROUND_ROBIN
tls:
mode: ISTIO_MUTUAL
---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: order-service
spec:
hosts:
- order-service
http:
- match:
- headers:
canary:
exact: "true"
route:
- destination:
host: order-service
subset: v2
weight: 50
- destination:
host: order-service
subset: v1
weight: 50
---
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: order-service-auth
spec:
selector:
matchLabels:
app: order-service
rules:
- from:
- source:
namespaces: ["default", "production"]
principals: ["cluster.local/ns/default/sa/order-sa"]
Microservices need to communicate with each other. Choosing the right communication pattern is critical for performance, reliability, and maintainability.
Synchronous HTTP/REST
Asynchronous Messaging
Event Sourcing
CQRS
# Asynchronous Messaging with Kafka
# Producer (Order Service)
@EventListener
public void onOrderCreated(OrderCreatedEvent event) {
kafkaTemplate.send("order-events", event);
}
# Consumer (Inventory Service)
@KafkaListener(topics = "order-events")
public void handleOrderCreated(OrderCreatedEvent event) {
inventoryService.reserveStock(event.getOrderId(), event.getItems());
}
# Event Sourcing Example
@EventSourcingHandler
public void handle(OrderCreatedEvent event) {
this.id = event.getOrderId();
this.customerId = event.getCustomerId();
this.items = event.getItems();
this.status = OrderStatus.CREATED;
}
@EventHandler
public void on(OrderCompletedEvent event) {
// Update read model
orderReadRepository.updateStatus(event.getOrderId(), event.getStatus());
}
Data management is one of the most challenging aspects of microservices. Each service should own its data, but this introduces complexity for distributed transactions and queries.
Database per Service
Saga Pattern
API Composition
Eventual Consistency
# Saga Pattern Example (Orchestration)
@Saga
public class OrderSaga {
@Step(compensation = "cancelOrder")
public void createOrder(OrderCreateRequest request) {
orderService.createOrder(request);
}
@Step(compensation = "rollbackInventory")
public void reserveInventory(OrderCreateRequest request) {
inventoryService.reserveStock(request.getOrderId(), request.getItems());
}
@Step(compensation = "refundPayment")
public void processPayment(OrderCreateRequest request) {
paymentService.processPayment(request.getOrderId(), request.getTotal());
}
@Step
public void completeOrder(OrderCreateRequest request) {
orderService.completeOrder(request.getOrderId());
}
// Compensation methods
public void cancelOrder(OrderCreateRequest request) {
orderService.cancelOrder(request.getOrderId());
}
public void rollbackInventory(OrderCreateRequest request) {
inventoryService.releaseStock(request.getOrderId(), request.getItems());
}
public void refundPayment(OrderCreateRequest request) {
paymentService.refundPayment(request.getOrderId());
}
}
Kubernetes is the ideal platform for running microservices. It provides service discovery, load balancing, scaling, and self-healing capabilities out of the box.
# Service Definition
apiVersion: v1
kind: Service
metadata:
name: order-service
spec:
selector:
app: order-service
ports:
- port: 8080
targetPort: 8080
---
# Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 3
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
spec:
containers:
- name: order-service
image: myregistry/order-service:latest
ports:
- containerPort: 8080
env:
- name: DB_URL
valueFrom:
secretKeyRef:
name: order-db-secret
key: url
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 20
periodSeconds: 5
---
# HPA for auto-scaling
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: order-service-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: order-service
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
- Use namespaces for environment isolation (dev, staging, production)
- Implement health checks (liveness and readiness probes)
- Use resource requests and limits for predictable performance
- Configure HPA for automatic scaling based on metrics
- Use Service Mesh (Istio, Linkerd) for advanced traffic management
- Implement PodDisruptionBudgets for high availability
Microservices architecture is a powerful approach for building scalable, resilient distributed systems. Focus on proper service boundaries, invest in observability, and embrace eventual consistency for successful implementation.