Agent Skills: Scaling Patterns Skill

Enterprise scaling patterns for microservices, event-driven architecture, and distributed systems

UncategorizedID: pluginagentmarketplace/custom-plugin-api-design/scaling-patterns

Skill Files

Browse the full folder contents for scaling-patterns.

Download Skill

Loading file tree…

skills/scaling-patterns/SKILL.md

Skill Metadata

Name
scaling-patterns
Description
Enterprise scaling patterns for microservices, event-driven architecture, and distributed systems

Scaling Patterns Skill

Purpose

Design and implement scalable distributed systems with proven patterns.

Microservices Architecture

Service Decomposition

┌─────────────────────────────────────────────────────────────────┐
│                        API Gateway                               │
│  (Authentication, Rate Limiting, Routing, Load Balancing)       │
└─────────────────────────┬───────────────────────────────────────┘
                          │
         ┌────────────────┼────────────────┐
         │                │                │
    ┌────▼────┐     ┌────▼────┐     ┌────▼────┐
    │  User   │     │  Order  │     │ Product │
    │ Service │     │ Service │     │ Service │
    └────┬────┘     └────┬────┘     └────┬────┘
         │               │                │
    ┌────▼────┐     ┌────▼────┐     ┌────▼────┐
    │ User DB │     │Order DB │     │Prod. DB │
    └─────────┘     └─────────┘     └─────────┘

Service Communication

// Synchronous (REST/gRPC)
class OrderService {
  constructor(
    private userClient: UserServiceClient,
    private productClient: ProductServiceClient,
  ) {}

  async createOrder(userId: string, productId: string) {
    // Validate user exists
    const user = await this.userClient.getUser(userId);
    if (!user) throw new NotFoundError('User not found');

    // Check product availability
    const product = await this.productClient.getProduct(productId);
    if (!product.available) throw new ConflictError('Product unavailable');

    return this.orderRepository.create({ userId, productId });
  }
}

// Asynchronous (Event-driven)
class OrderService {
  async createOrder(userId: string, productId: string) {
    const order = await this.orderRepository.create({
      userId,
      productId,
      status: 'pending',
    });

    // Emit event instead of sync call
    await this.eventBus.publish('order.created', {
      orderId: order.id,
      userId,
      productId,
      timestamp: new Date(),
    });

    return order;
  }
}

SAGA Pattern

Choreography-based SAGA

// Event handlers in each service

// Order Service
class OrderEventHandler {
  @EventHandler('order.created')
  async onOrderCreated(event: OrderCreatedEvent) {
    // Emit next event in chain
    await this.eventBus.publish('payment.requested', {
      orderId: event.orderId,
      amount: event.total,
    });
  }

  @EventHandler('payment.failed')
  async onPaymentFailed(event: PaymentFailedEvent) {
    // Compensating transaction
    await this.orderRepository.update(event.orderId, { status: 'cancelled' });
    await this.eventBus.publish('order.cancelled', { orderId: event.orderId });
  }
}

// Payment Service
class PaymentEventHandler {
  @EventHandler('payment.requested')
  async onPaymentRequested(event: PaymentRequestedEvent) {
    try {
      await this.paymentGateway.charge(event.amount);
      await this.eventBus.publish('payment.completed', {
        orderId: event.orderId,
      });
    } catch (error) {
      await this.eventBus.publish('payment.failed', {
        orderId: event.orderId,
        reason: error.message,
      });
    }
  }
}

Orchestration-based SAGA

class OrderSaga {
  private steps: SagaStep[] = [
    {
      name: 'reserveInventory',
      execute: (ctx) => this.inventoryService.reserve(ctx.productId, ctx.quantity),
      compensate: (ctx) => this.inventoryService.release(ctx.productId, ctx.quantity),
    },
    {
      name: 'processPayment',
      execute: (ctx) => this.paymentService.charge(ctx.userId, ctx.amount),
      compensate: (ctx) => this.paymentService.refund(ctx.paymentId),
    },
    {
      name: 'createShipment',
      execute: (ctx) => this.shippingService.create(ctx.orderId, ctx.address),
      compensate: (ctx) => this.shippingService.cancel(ctx.shipmentId),
    },
  ];

  async execute(context: OrderContext) {
    const completedSteps: SagaStep[] = [];

    for (const step of this.steps) {
      try {
        const result = await step.execute(context);
        context = { ...context, ...result };
        completedSteps.push(step);
      } catch (error) {
        // Rollback in reverse order
        for (const completedStep of completedSteps.reverse()) {
          await completedStep.compensate(context);
        }
        throw new SagaExecutionError(step.name, error);
      }
    }

    return context;
  }
}

Circuit Breaker Pattern

enum CircuitState {
  CLOSED = 'CLOSED',
  OPEN = 'OPEN',
  HALF_OPEN = 'HALF_OPEN',
}

class CircuitBreaker {
  private state = CircuitState.CLOSED;
  private failureCount = 0;
  private successCount = 0;
  private lastFailureTime?: Date;

  constructor(
    private readonly options: {
      failureThreshold: number;      // Failures before opening
      successThreshold: number;      // Successes in half-open to close
      timeout: number;               // Time in open state before half-open
    }
  ) {}

  async execute<T>(fn: () => Promise<T>): Promise<T> {
    if (this.state === CircuitState.OPEN) {
      if (this.shouldAttemptReset()) {
        this.state = CircuitState.HALF_OPEN;
      } else {
        throw new CircuitBreakerOpenError();
      }
    }

    try {
      const result = await fn();
      this.onSuccess();
      return result;
    } catch (error) {
      this.onFailure();
      throw error;
    }
  }

  private onSuccess() {
    this.failureCount = 0;
    if (this.state === CircuitState.HALF_OPEN) {
      this.successCount++;
      if (this.successCount >= this.options.successThreshold) {
        this.state = CircuitState.CLOSED;
        this.successCount = 0;
      }
    }
  }

  private onFailure() {
    this.failureCount++;
    this.lastFailureTime = new Date();
    if (this.failureCount >= this.options.failureThreshold) {
      this.state = CircuitState.OPEN;
    }
  }

  private shouldAttemptReset(): boolean {
    if (!this.lastFailureTime) return false;
    const elapsed = Date.now() - this.lastFailureTime.getTime();
    return elapsed >= this.options.timeout;
  }
}

// Usage
const userServiceBreaker = new CircuitBreaker({
  failureThreshold: 5,
  successThreshold: 3,
  timeout: 30000,
});

async function getUser(id: string) {
  return userServiceBreaker.execute(() => userService.get(id));
}

Event Sourcing & CQRS

// Event Store
interface DomainEvent {
  id: string;
  aggregateId: string;
  aggregateType: string;
  eventType: string;
  payload: unknown;
  timestamp: Date;
  version: number;
}

class EventStore {
  async append(events: DomainEvent[]): Promise<void> {
    await this.db.transaction(async (tx) => {
      for (const event of events) {
        await tx.insert('events', event);
      }
    });

    // Publish to message bus
    for (const event of events) {
      await this.messageBus.publish(event.eventType, event);
    }
  }

  async getEvents(aggregateId: string, fromVersion = 0): Promise<DomainEvent[]> {
    return this.db.query(
      'SELECT * FROM events WHERE aggregate_id = ? AND version > ? ORDER BY version',
      [aggregateId, fromVersion]
    );
  }
}

// Aggregate
class OrderAggregate {
  private events: DomainEvent[] = [];
  private state: OrderState = { status: 'draft', items: [] };

  static async load(eventStore: EventStore, id: string): Promise<OrderAggregate> {
    const aggregate = new OrderAggregate();
    const events = await eventStore.getEvents(id);
    events.forEach((e) => aggregate.apply(e, false));
    return aggregate;
  }

  addItem(productId: string, quantity: number): void {
    this.apply({
      eventType: 'ItemAdded',
      payload: { productId, quantity },
    });
  }

  submit(): void {
    if (this.state.items.length === 0) {
      throw new Error('Cannot submit empty order');
    }
    this.apply({ eventType: 'OrderSubmitted', payload: {} });
  }

  private apply(event: Partial<DomainEvent>, isNew = true): void {
    // Update state based on event
    switch (event.eventType) {
      case 'ItemAdded':
        this.state.items.push(event.payload);
        break;
      case 'OrderSubmitted':
        this.state.status = 'submitted';
        break;
    }

    if (isNew) {
      this.events.push(event as DomainEvent);
    }
  }

  getUncommittedEvents(): DomainEvent[] {
    return this.events;
  }
}

// CQRS: Separate read model
class OrderReadModel {
  async project(event: DomainEvent): Promise<void> {
    switch (event.eventType) {
      case 'OrderSubmitted':
        await this.db.query(
          'UPDATE orders_view SET status = ?, submitted_at = ? WHERE id = ?',
          ['submitted', event.timestamp, event.aggregateId]
        );
        break;
    }
  }
}

AI Agent Integration Patterns

// Tool-use pattern for AI agents
interface Tool {
  name: string;
  description: string;
  parameters: JSONSchema;
  execute: (params: unknown) => Promise<unknown>;
}

class AIAgentService {
  private tools: Map<string, Tool> = new Map();

  registerTool(tool: Tool): void {
    this.tools.set(tool.name, tool);
  }

  async executeWorkflow(prompt: string): Promise<AgentResponse> {
    const messages: Message[] = [{ role: 'user', content: prompt }];

    while (true) {
      const response = await this.llm.chat({
        messages,
        tools: Array.from(this.tools.values()),
      });

      if (response.stopReason === 'end_turn') {
        return { result: response.content, steps: messages };
      }

      if (response.stopReason === 'tool_use') {
        const toolCalls = response.toolCalls;
        const results = await Promise.all(
          toolCalls.map(async (call) => {
            const tool = this.tools.get(call.name);
            if (!tool) throw new Error(`Unknown tool: ${call.name}`);
            return {
              toolCallId: call.id,
              result: await tool.execute(call.parameters),
            };
          })
        );

        messages.push(
          { role: 'assistant', content: response.content, toolCalls },
          { role: 'tool', toolResults: results }
        );
      }
    }
  }
}

// Register tools
agentService.registerTool({
  name: 'search_orders',
  description: 'Search orders by customer or status',
  parameters: {
    type: 'object',
    properties: {
      customerId: { type: 'string' },
      status: { type: 'string', enum: ['pending', 'shipped', 'delivered'] },
    },
  },
  execute: async (params) => orderService.search(params),
});

Unit Test Template

import { describe, it, expect, vi, beforeEach } from 'vitest';

describe('Scaling Patterns', () => {
  describe('Circuit Breaker', () => {
    let breaker: CircuitBreaker;

    beforeEach(() => {
      breaker = new CircuitBreaker({
        failureThreshold: 3,
        successThreshold: 2,
        timeout: 1000,
      });
    });

    it('should open after failure threshold', async () => {
      const failingFn = vi.fn().mockRejectedValue(new Error('fail'));

      for (let i = 0; i < 3; i++) {
        await expect(breaker.execute(failingFn)).rejects.toThrow();
      }

      await expect(breaker.execute(failingFn)).rejects.toThrow(CircuitBreakerOpenError);
    });

    it('should close after success threshold in half-open', async () => {
      // Open the circuit
      const failingFn = vi.fn().mockRejectedValue(new Error('fail'));
      for (let i = 0; i < 3; i++) {
        await expect(breaker.execute(failingFn)).rejects.toThrow();
      }

      // Wait for timeout
      await new Promise((r) => setTimeout(r, 1100));

      // Succeed twice to close
      const successFn = vi.fn().mockResolvedValue('ok');
      await breaker.execute(successFn);
      await breaker.execute(successFn);

      expect(breaker.state).toBe(CircuitState.CLOSED);
    });
  });

  describe('SAGA', () => {
    it('should rollback on failure', async () => {
      const saga = new OrderSaga();
      const compensateSpy = vi.fn();

      saga.steps[1].compensate = compensateSpy;
      saga.steps[2].execute = vi.fn().mockRejectedValue(new Error('fail'));

      await expect(saga.execute({})).rejects.toThrow(SagaExecutionError);
      expect(compensateSpy).toHaveBeenCalled();
    });
  });

  describe('Event Sourcing', () => {
    it('should rebuild state from events', async () => {
      const events = [
        { eventType: 'ItemAdded', payload: { productId: '1', quantity: 2 } },
        { eventType: 'ItemAdded', payload: { productId: '2', quantity: 1 } },
        { eventType: 'OrderSubmitted', payload: {} },
      ];

      const aggregate = OrderAggregate.fromEvents(events);

      expect(aggregate.state.items).toHaveLength(2);
      expect(aggregate.state.status).toBe('submitted');
    });
  });
});

Troubleshooting

| Issue | Cause | Solution | |-------|-------|----------| | Cascading failures | No circuit breaker | Implement circuit breaker pattern | | Data inconsistency | Distributed transaction | Use SAGA with compensation | | Event ordering issues | Async processing | Use sequence numbers, idempotency | | Slow event replay | Too many events | Use snapshots | | Lost messages | No retry mechanism | Use durable queues, dead-letter |


Quality Checklist

  • [ ] Service boundaries clearly defined
  • [ ] Communication patterns chosen (sync/async)
  • [ ] SAGA implemented for distributed transactions
  • [ ] Circuit breakers in place
  • [ ] Event sourcing with snapshots (if needed)
  • [ ] Idempotency for all handlers
  • [ ] Distributed tracing configured
  • [ ] Dead-letter queues set up
  • [ ] Monitoring and alerting
  • [ ] Chaos engineering tests