Skip to main content

Agent Skills Framework Extension

Microservices Patterns Skill

When to Use This Skill

Use this skill when implementing microservices patterns patterns in your codebase.

How to Use This Skill

  1. Review the patterns and examples below
  2. Apply the relevant patterns to your implementation
  3. Follow the best practices outlined in this skill

Service mesh, API gateway, service discovery, distributed tracing, and inter-service communication patterns.

Core Capabilities

  1. Service Decomposition - Bounded context, domain-driven design
  2. API Gateway - Kong, routing, rate limiting
  3. Service Discovery - Consul, Kubernetes DNS
  4. Communication - gRPC, REST, async messaging
  5. Saga Pattern - Distributed transaction coordination

Service Decomposition

Bounded Context Mapping

// Domain-driven design approach to service boundaries

/*
┌────────────────────────────────────────────────────────────┐
│ E-Commerce Platform │
├──────────────┬──────────────┬──────────────┬───────────────┤
│ User │ Catalog │ Order │ Payment │
│ Context │ Context │ Context │ Context │
├──────────────┼──────────────┼──────────────┼───────────────┤
│ - User │ - Product │ - Order │ - Payment │
│ - Profile │ - Category │ - OrderItem │ - Transaction │
│ - Auth │ - Inventory │ - Shipment │ - Refund │
│ - Preference │ - Price │ - Cart │ - Wallet │
└──────────────┴──────────────┴──────────────┴───────────────┘

Each context = 1 Microservice with its own:
- Database (schema isolation)
- API surface
- Domain models
- Deployment lifecycle
*/

// Anti-corruption layer between contexts
interface UserContext {
User: { id: string; email: string; name: string };
}

interface OrderContext {
// Simplified view of User for Order context
Customer: { id: string; name: string; shippingAddress: string };
}

// Adapter to translate between contexts
class UserToCustomerAdapter {
constructor(private readonly userService: UserServiceClient) {}

async getCustomer(userId: string): Promise<OrderContext['Customer']> {
const user = await this.userService.getUser(userId);
const profile = await this.userService.getProfile(userId);

return {
id: user.id,
name: user.name,
shippingAddress: profile.defaultShippingAddress,
};
}
}

API Gateway (Kong)

# kong.yml - Declarative configuration
_format_version: "3.0"

services:
- name: user-service
url: http://user-service:8080
routes:
- name: user-routes
paths:
- /api/v1/users
strip_path: false
plugins:
- name: rate-limiting
config:
minute: 100
policy: redis
redis_host: redis
- name: jwt
config:
key_claim_name: kid

- name: order-service
url: http://order-service:8080
routes:
- name: order-routes
paths:
- /api/v1/orders
plugins:
- name: rate-limiting
config:
minute: 50
- name: jwt
- name: request-transformer
config:
add:
headers:
- "X-Request-ID:$(uuid)"

- name: catalog-service
url: http://catalog-service:8080
routes:
- name: catalog-routes
paths:
- /api/v1/products
- /api/v1/categories
plugins:
- name: proxy-cache
config:
content_type:
- application/json
cache_ttl: 300
strategy: memory

plugins:
- name: correlation-id
config:
header_name: X-Correlation-ID
generator: uuid
echo_downstream: true

- name: prometheus
config:
status_code_metrics: true
latency_metrics: true

Service Discovery (Consul)

// src/discovery/consul.service.ts
import Consul from 'consul';

interface ServiceInstance {
id: string;
name: string;
address: string;
port: number;
healthy: boolean;
}

export class ServiceDiscovery {
private consul: Consul.Consul;
private registeredServices: Set<string> = new Set();

constructor(host: string, port: number) {
this.consul = new Consul({ host, port });
}

async register(service: {
name: string;
id: string;
address: string;
port: number;
healthCheckUrl: string;
}): Promise<void> {
await this.consul.agent.service.register({
id: service.id,
name: service.name,
address: service.address,
port: service.port,
check: {
http: service.healthCheckUrl,
interval: '10s',
timeout: '5s',
deregistercriticalserviceafter: '1m',
},
tags: ['api', 'v1'],
});

this.registeredServices.add(service.id);

// Graceful shutdown
process.on('SIGTERM', () => this.deregister(service.id));
process.on('SIGINT', () => this.deregister(service.id));
}

async deregister(serviceId: string): Promise<void> {
await this.consul.agent.service.deregister(serviceId);
this.registeredServices.delete(serviceId);
}

async discover(serviceName: string): Promise<ServiceInstance[]> {
const result = await this.consul.health.service({
service: serviceName,
passing: true, // Only healthy instances
});

return result.map((entry: any) => ({
id: entry.Service.ID,
name: entry.Service.Service,
address: entry.Service.Address,
port: entry.Service.Port,
healthy: entry.Checks.every((c: any) => c.Status === 'passing'),
}));
}

// Round-robin load balancer
private instanceIndex: Map<string, number> = new Map();

async getNextInstance(serviceName: string): Promise<ServiceInstance | null> {
const instances = await this.discover(serviceName);
if (instances.length === 0) return null;

const currentIndex = this.instanceIndex.get(serviceName) ?? 0;
const instance = instances[currentIndex % instances.length];
this.instanceIndex.set(serviceName, currentIndex + 1);

return instance;
}
}

gRPC Communication

// proto/order.proto
syntax = "proto3";

package order;

service OrderService {
rpc CreateOrder(CreateOrderRequest) returns (Order);
rpc GetOrder(GetOrderRequest) returns (Order);
rpc ListOrders(ListOrdersRequest) returns (stream Order);
rpc UpdateOrderStatus(UpdateStatusRequest) returns (Order);
}

message CreateOrderRequest {
string customer_id = 1;
repeated OrderItem items = 2;
string shipping_address = 3;
}

message OrderItem {
string product_id = 1;
int32 quantity = 2;
int64 unit_price_cents = 3;
}

message Order {
string id = 1;
string customer_id = 2;
repeated OrderItem items = 3;
OrderStatus status = 4;
int64 total_cents = 5;
string created_at = 6;
}

enum OrderStatus {
PENDING = 0;
CONFIRMED = 1;
SHIPPED = 2;
DELIVERED = 3;
CANCELLED = 4;
}
// src/grpc/order.client.ts
import * as grpc from '@grpc/grpc-js';
import * as protoLoader from '@grpc/proto-loader';

const PROTO_PATH = 'proto/order.proto';

const packageDefinition = protoLoader.loadSync(PROTO_PATH, {
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true,
});

const orderProto = grpc.loadPackageDefinition(packageDefinition).order as any;

export class OrderServiceClient {
private client: any;

constructor(address: string) {
this.client = new orderProto.OrderService(
address,
grpc.credentials.createInsecure()
);
}

createOrder(request: {
customerId: string;
items: Array<{ productId: string; quantity: number; unitPriceCents: number }>;
shippingAddress: string;
}): Promise<any> {
return new Promise((resolve, reject) => {
this.client.CreateOrder(
{
customer_id: request.customerId,
items: request.items.map(item => ({
product_id: item.productId,
quantity: item.quantity,
unit_price_cents: item.unitPriceCents,
})),
shipping_address: request.shippingAddress,
},
(err: Error | null, response: any) => {
if (err) reject(err);
else resolve(response);
}
);
});
}

getOrder(orderId: string): Promise<any> {
return new Promise((resolve, reject) => {
this.client.GetOrder(
{ id: orderId },
(err: Error | null, response: any) => {
if (err) reject(err);
else resolve(response);
}
);
});
}
}

Saga Pattern

// src/saga/order-saga.ts
interface SagaStep<TData> {
name: string;
execute: (data: TData) => Promise<unknown>;
compensate: (data: TData) => Promise<void>;
}

export class Saga<TData> {
private steps: SagaStep<TData>[] = [];
private completedSteps: string[] = [];

addStep(step: SagaStep<TData>): this {
this.steps.push(step);
return this;
}

async execute(data: TData): Promise<void> {
for (const step of this.steps) {
try {
console.log(`Executing step: ${step.name}`);
await step.execute(data);
this.completedSteps.push(step.name);
} catch (error) {
console.error(`Step ${step.name} failed:`, error);
await this.compensate(data);
throw error;
}
}
}

private async compensate(data: TData): Promise<void> {
// Reverse order compensation
for (let i = this.completedSteps.length - 1; i >= 0; i--) {
const stepName = this.completedSteps[i];
const step = this.steps.find(s => s.name === stepName);

if (step) {
try {
console.log(`Compensating step: ${step.name}`);
await step.compensate(data);
} catch (err) {
console.error(`Compensation failed for ${step.name}:`, err);
// Log but continue compensating other steps
}
}
}
}
}

// Order creation saga
const orderSaga = new Saga<OrderData>()
.addStep({
name: 'reserve-inventory',
execute: async (data) => {
data.reservationId = await inventoryService.reserve(data.items);
},
compensate: async (data) => {
await inventoryService.release(data.reservationId);
},
})
.addStep({
name: 'process-payment',
execute: async (data) => {
data.transactionId = await paymentService.charge(
data.customerId,
data.total
);
},
compensate: async (data) => {
await paymentService.refund(data.transactionId);
},
})
.addStep({
name: 'create-order',
execute: async (data) => {
data.orderId = await orderService.create(data);
},
compensate: async (data) => {
await orderService.cancel(data.orderId);
},
})
.addStep({
name: 'notify-customer',
execute: async (data) => {
await notificationService.sendOrderConfirmation(data);
},
compensate: async () => {
// No compensation needed for notifications
},
});

// Execute saga
await orderSaga.execute(orderData);

Usage Examples

Design Microservices Architecture

Apply microservices-patterns skill to decompose a monolith into bounded contexts with API gateway

Implement Service Discovery

Apply microservices-patterns skill to add Consul-based service discovery with health checks

Add gRPC Communication

Apply microservices-patterns skill to implement gRPC communication between order and payment services

Service Communication Decision Matrix

Choose the right communication pattern for your use case:

Use CasePatternProtocolWhen to UseWhen NOT to Use
Query dataRequest-ResponseREST/gRPCSimple reads, immediate responseHigh-latency tolerance
Command with responseRequest-ResponsegRPCType-safe, low latencySimple CRUD
Fire and forgetAsync MessagingRabbitMQ/KafkaBackground jobs, notificationsImmediate confirmation needed
Event broadcastPub/SubKafkaMultiple consumers, event sourcingSingle consumer
Long-running workflowSaga/ChoreographyEventsDistributed transactionsSimple transactions
Bi-directional streamStreaminggRPC/WebSocketReal-time updates, large datasetsSimple request/response

Communication Selection Flowchart:

Does caller need immediate response?

├── Yes → Is response time-critical (<100ms)?
│ │
│ ├── Yes → gRPC (binary, efficient)
│ │
│ └── No → REST (simpler, cacheable)

└── No → Is ordering important?

├── Yes → Kafka (ordered partitions)

└── No → RabbitMQ (simpler, flexible routing)

Anti-Pattern Detection:

If You See This...It's Likely...Fix With...
Service A → B → C → D (sync chain)Cascading failuresAsync messaging, circuit breakers
All services poll for updatesWasted resourcesEvent-driven (pub/sub)
Services share databaseDistributed monolithDatabase per service
No retry/timeout on HTTP callsSilent failuresResilience patterns
Synchronous calls in saga stepsBlocked transactionsAsync compensation

Protocol Comparison:

AspectRESTgRPCKafkaRabbitMQ
LatencyMediumLowMediumMedium
ThroughputMediumHighVery HighHigh
SchemaOptional (OpenAPI)Required (Protobuf)Optional (Avro)Optional
Browser supportNativeVia proxyN/AVia WebSocket
DebuggingEasyHarderHarderMedium

Integration Points

  • system-architecture-design - Overall architecture patterns
  • container-orchestration - Kubernetes deployment
  • monitoring-observability - Distributed tracing
  • event-driven-architecture - Async communication

Success Output

When successful, this skill MUST output:

✅ SKILL COMPLETE: microservices-patterns

Completed:
- [x] Service decomposition with bounded contexts defined
- [x] API Gateway (Kong) configured with routing
- [x] Service discovery (Consul) implemented
- [x] gRPC communication established between services
- [x] Saga pattern for distributed transactions

Outputs:
- Service boundaries: user, catalog, order, payment contexts
- kong.yml (API gateway configuration)
- src/discovery/consul.service.ts
- proto/order.proto (gRPC schema)
- src/saga/order-saga.ts
- Services registered: 4/4 healthy in Consul
- Gateway routes: 100% coverage

Completion Checklist

Before marking this skill as complete, verify:

  • Bounded contexts defined with clear separation
  • API Gateway routes all services correctly
  • Service discovery health checks passing
  • gRPC service-to-service communication working
  • Saga compensations tested (rollback scenarios)
  • Anti-corruption layers between contexts implemented
  • Circuit breakers configured for inter-service calls
  • Distributed tracing propagates context correctly

Failure Indicators

This skill has FAILED if:

  • ❌ Services share database schemas (bounded context violation)
  • ❌ API Gateway routing errors or missing rate limiting
  • ❌ Service discovery fails to find healthy instances
  • ❌ gRPC calls fail with connection errors
  • ❌ Saga compensations don't execute in reverse order
  • ❌ Services have circular dependencies
  • ❌ No health checks configured (blind deployments)

When NOT to Use

Do NOT use this skill when:

  • Building simple CRUD application (monolith is fine)
  • Team size <5 developers (microservices overhead not justified)
  • Domain not well-understood (premature decomposition)
  • Shared database required for transactions (consider modular monolith)
  • Real-time consistency critical (distributed systems have eventual consistency)

Use alternatives:

  • Modular monolith for medium-complexity apps with clear modules
  • Serverless functions for event-driven, independent workloads
  • Traditional n-tier architecture for simple business apps
  • Peer-to-peer communication without gateway for small service counts

Anti-Patterns (Avoid)

Anti-PatternProblemSolution
Nano-servicesToo many tiny servicesBounded contexts, not function-level services
Shared databaseTight couplingDatabase per service
Synchronous chainsCascading failuresAsync messaging for non-critical paths
No circuit breakersFailure amplificationImplement circuit breakers on all calls
Missing distributed tracingDebugging nightmareOpenTelemetry from day 1
Distributed monolithWorst of both worldsEnsure true service independence
Ignoring data consistencyData corruptionUse sagas or event sourcing

Principles

This skill embodies:

  • #2 First Principles - Domain-driven design, bounded contexts from business domains
  • #4 Separation of Concerns - Each service owns its data, API, and domain logic
  • #5 Eliminate Ambiguity - Clear service boundaries via bounded contexts
  • #6 Clear, Understandable, Explainable - Service discovery, gateway routing documented
  • #7 Validate Continuously - Health checks, circuit breakers, distributed tracing
  • #8 No Assumptions - Always verify service availability before calling
  • Resilience by Design - Saga compensations, retry logic, fallback mechanisms
  • Independent Deployability - Services deployed without coordinating releases