Introduction
Microservices architecture has evolved significantly. In 2026, organizations have moved beyond initial adoption to refining their approaches, learning from production experiences, and establishing well-documented patterns. The focus has shifted from whether to use microservices to how to do it well.
This guide covers essential microservices patterns in 2026, from decomposition strategies to operational patterns. Whether you’re designing a new system or improving existing services, this guide provides practical insights.
Service Decomposition
When to Decompose
Good candidates for microservices:
- Independent deployability requirements
- Different scaling needs
- Different team ownership domains
- Technology heterogeneity needs
- Business capability boundaries
Avoid for:
- Simple applications
- Tightly coupled business logic
- Small teams
- Performance-critical synchronous calls
Decomposition Strategies
# Kubernetes deployment for microservice
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 3
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
version: v1
spec:
containers:
- name: order-service
image: myregistry/order-service:v1.2.3
ports:
- containerPort: 8080
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: db-credentials
key: url
resources:
limits:
memory: "512Mi"
cpu: "500m"
Domain-Driven Design
// Bounded context - Order Service
namespace OrderDomain {
interface Order {
id: string;
customerId: string;
items: OrderItem[];
status: OrderStatus;
total: Money;
createdAt: Date;
}
enum OrderStatus {
PENDING = "PENDING",
CONFIRMED = "CONFIRMED",
SHIPPED = "SHIPPED",
DELIVERED = "DELIVERED",
CANCELLED = "CANCELLED"
}
interface OrderItem {
productId: string;
quantity: number;
price: Money;
}
interface OrderService {
createOrder(customerId: string, items: OrderItem[]): Promise<Order>;
confirmOrder(orderId: string): Promise<Order>;
cancelOrder(orderId: string): Promise<Order>;
getOrder(orderId: string): Promise<Order>;
getOrdersByCustomer(customerId: string): Promise<Order[]>;
}
}
Communication Patterns
Synchronous Communication
// REST client
class OrderClient {
private httpClient: AxiosInstance;
async getOrder(orderId: string): Promise<Order> {
const response = await this.httpClient.get(`/orders/${orderId}`);
return response.data;
}
async createOrder(order: CreateOrderRequest): Promise<Order> {
const response = await this.httpClient.post('/orders', order);
return response.data;
}
}
// gRPC service
syntax = "proto3";
package order;
service OrderService {
rpc GetOrder(GetOrderRequest) returns (Order);
rpc CreateOrder(CreateOrderRequest) returns (Order);
rpc StreamOrders(StreamOrdersRequest) returns (stream Order);
}
message GetOrderRequest {
string order_id = 1;
}
Asynchronous Communication
// Message publisher
class OrderEventPublisher {
private publisher: KafkaProducer;
async publishOrderCreated(order: Order): Promise<void> {
const event = {
type: 'ORDER_CREATED',
payload: order,
timestamp: new Date().toISOString(),
correlationId: order.id
};
await this.publisher.send({
topic: 'order-events',
messages: [{
key: order.id,
value: JSON.stringify(event)
}]
});
}
}
// Event consumer
class OrderEventHandler {
@ProcessOrder('ORDER_CREATED')
async handleOrderCreated(event: OrderEvent): Promise<void> {
const order = event.payload;
// Update analytics
await this.analyticsService.trackOrder(order);
// Send notification
await this.notificationService.sendOrderConfirmation(order);
// Reserve inventory
await this.inventoryService.reserveItems(order.items);
}
}
Service Mesh
# Istio virtual service - canary deployment
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: order-service
spec:
hosts:
- order-service
http:
- match:
- headers:
x-canary:
exact: "true"
route:
- destination:
host: order-service
subset: v2
weight: 100
- route:
- destination:
host: order-service
subset: v1
weight: 100
API Gateway
Edge Layer
# Kong gateway configuration
_format_version: "3.0"
services:
- name: order-service
url: http://order-service:8080
routes:
- name: orders
paths:
- /api/orders
methods:
- GET
- POST
plugins:
- name: rate-limiting
config:
minute: 100
policy: redis
redis_host: redis
- name: cors
config:
origins:
- "https://example.com"
methods:
- GET
- POST
- OPTIONS
headers:
- Authorization
- Content-Type
- name: jwt
config:
key_claim_name: kid
- name: payment-service
url: http://payment-service:8080
routes:
- name: payments
paths:
- /api/payments
plugins:
- name: prometheus
config:
per_consumer: false
BFF Pattern
// Backend for Frontend
class MobileBFF {
private orderClient: OrderClient;
private userClient: UserClient;
private inventoryClient: InventoryClient;
async getHomeScreenData(userId: string): Promise<MobileHomeData> {
// Fetch in parallel
const [user, recentOrders, recommendations] = await Promise.all([
this.userClient.getProfile(userId),
this.orderClient.getRecentOrders(userId),
this.inventoryClient.getRecommendations(userId)
]);
return {
user: {
name: user.name,
avatar: user.avatar
},
recentOrders: recentOrders.map(o => ({
id: o.id,
status: o.status,
total: o.total
})),
recommendations: recommendations.slice(0, 10)
};
}
}
Data Management
Database per Service
// Each service owns its data
// Order Service Database (PostgreSQL)
class OrderRepository {
async findById(id: string): Promise<Order | null> {
return this.db.query(
'SELECT * FROM orders WHERE id = $1',
[id]
).then(r => r.rows[0]);
}
}
// Inventory Service Database (MongoDB)
class InventoryRepository {
async findByProductId(productId: string): Promise<Inventory> {
return this.collection.findOne({ productId });
}
}
CQRS Pattern
// Command side - write model
class OrderCommandHandler {
async handleCreateOrder(cmd: CreateOrderCommand): Promise<Order> {
// Validate
const inventory = await this.inventoryQuery.getAvailable(cmd.items);
if (!inventory.available) {
throw new InsufficientInventoryError();
}
// Create order
const order = new Order({
...cmd,
status: OrderStatus.PENDING
});
await this.orderStore.save(order);
// Publish event
await this.eventBus.publish(new OrderCreatedEvent(order));
return order;
}
}
// Query side - read model
class OrderQueryHandler {
async getOrderSummary(orderId: string): Promise<OrderSummary> {
// Read from optimized read model
return this.readModel.findById(orderId);
}
async getCustomerOrders(customerId: string): Promise<OrderSummary[]> {
return this.readModel.findByCustomer(customerId);
}
}
Saga Pattern
// Orchestration-based saga
class OrderSaga {
async execute(order: CreateOrderCommand): Promise<Order> {
try {
// Step 1: Create order (pending)
const order = await this.orderService.create(order);
// Step 2: Reserve inventory
await this.inventoryService.reserve(order.items);
// Step 3: Process payment
await this.paymentService.charge(order.customerId, order.total);
// Step 4: Confirm order
return await this.orderService.confirm(order.id);
} catch (error) {
// Compensating transactions
await this.compensate(order);
throw error;
}
}
private async compensate(order: Order): Promise<void> {
// Release inventory
await this.inventoryService.release(order.items).catch(() => {});
// Refund payment
await this.paymentService.refund(order.id).catch(() => {});
// Cancel order
await this.orderService.cancel(order.id).catch(() => {});
}
}
Service Discovery
Registration
// Service registration
class ServiceRegistry {
async register(service: ServiceInstance): Promise<void> {
await this.etcd.put(
`/services/${service.name}/${service.id}`,
JSON.stringify({
...service,
registeredAt: new Date(),
healthCheck: service.healthCheckUrl
}),
{ ttl: 30 } // Heartbeat every 30 seconds
);
}
async discover(serviceName: string): Promise<ServiceInstance[]> {
const response = await this.etcd.getAll(
`/services/${serviceName}/`
);
return response.kvs.map(kv => JSON.parse(kv.value.toString()));
}
}
Load Balancing
// Client-side load balancing
class LoadBalancer {
constructor(
private serviceRegistry: ServiceRegistry,
private strategy: LoadBalancingStrategy = new RoundRobinStrategy()
) {}
async getService(serviceName: string): Promise<ServiceInstance> {
const instances = await this.serviceRegistry.discover(serviceName);
// Filter healthy instances
const healthy = instances.filter(i => i.isHealthy());
return this.strategy.select(healthy);
}
}
// Client with ribbon
@FeignClient(name: 'order-service')
interface OrderClient {
@GetMapping('/orders/{id}')
getOrder(@PathVariable('id') id: string): Order;
}
Observability
Distributed Tracing
// OpenTelemetry instrumentation
import { trace, SpanStatusCode } from '@opentelemetry/api';
const tracer = trace.getTracer('order-service');
class OrderService {
async createOrder(cmd: CreateOrderCommand): Promise<Order> {
return tracer.startActiveSpan('createOrder', async (span) => {
try {
span.setAttribute('order.customerId', cmd.customerId);
span.setAttribute('order.itemCount', cmd.items.length);
// Validate
span.addEvent('Validating order');
await this.validator.validate(cmd);
// Create
span.addEvent('Creating order');
const order = await this.repository.create(cmd);
// Publish event
span.addEvent('Publishing event');
await this.eventBus.publish(new OrderCreated(order));
span.setStatus({ code: SpanStatusCode.OK });
return order;
} catch (error) {
span.setStatus({
code: SpanStatusCode.ERROR,
message: error.message
});
throw error;
} finally {
span.end();
}
});
}
}
Health Checks
# Kubernetes health probes
apiVersion: v1
kind: Pod
metadata:
name: order-service
spec:
containers:
- name: order-service
image: order-service:latest
livenessProbe:
httpGet:
path: /health/live
port: 8080
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /health/ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
External Resources
Documentation
- Microservices.io - Pattern catalog
- Microsoft Microservices - Azure patterns
- AWS Microservices - AWS patterns
Tools
Learning
- Building Microservices - Book by Sam Newman
- Domain-Driven Design - Eric Evans book
Conclusion
Microservices patterns in 2026 are well-established. The key is understanding trade-offs and choosing patterns that fit your organization and requirements.
Start with clear domain boundaries. Use asynchronous communication where possible. Implement observability from day one. And remember: microservices are a means to an end, not a goal in themselves.
The best architecture is the one that enables your team to deliver value effectively. Choose wisely.
Comments