| name | architecture-patterns |
| description | Architectural patterns and design principles for scalable, maintainable systems. Use when designing systems, refactoring architecture, or choosing patterns. |
Architecture Patterns Skill
Purpose
Guide architectural decisions with proven patterns for building scalable, maintainable, and testable systems.
Core Architectural Styles
Clean Architecture (Onion Architecture)
┌─────────────────────────────────────────────────────────────┐
│ Frameworks & Drivers │
│ ┌─────────────────────────────────────────────────────────┐│
│ │ Interface Adapters ││
│ │ ┌─────────────────────────────────────────────────────┐││
│ │ │ Application Business Rules │││
│ │ │ ┌─────────────────────────────────────────────────┐│││
│ │ │ │ Enterprise Business Rules ││││
│ │ │ │ (Domain/Entities) ││││
│ │ │ └─────────────────────────────────────────────────┘│││
│ │ └─────────────────────────────────────────────────────┘││
│ └─────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────┘
Key Principles:
- Dependencies point inward
- Inner layers know nothing about outer layers
- Domain entities are framework-agnostic
Layer Responsibilities:
| Layer | Contains | Depends On |
|---|---|---|
| Entities | Business objects, domain logic | Nothing |
| Use Cases | Application-specific logic | Entities |
| Adapters | Controllers, gateways, presenters | Use Cases, Entities |
| Frameworks | Web framework, database, UI | All inner layers |
Hexagonal Architecture (Ports & Adapters)
┌──────────────┐
│ Driving │
│ Adapter │
│ (REST API) │
└──────┬───────┘
│
┌──────▼───────┐
│ Port │
│ (Interface) │
└──────┬───────┘
│
┌──────────────────────────▼──────────────────────────┐
│ Application Core │
│ ┌────────────────────────────────────────────────┐ │
│ │ Domain Model │ │
│ │ (Entities + Business Rules) │ │
│ └────────────────────────────────────────────────┘ │
└──────────────────────────┬──────────────────────────┘
│
┌──────▼───────┐
│ Port │
│ (Interface) │
└──────┬───────┘
│
┌──────▼───────┐
│ Driven │
│ Adapter │
│ (Database) │
└──────────────┘
Key Concepts:
- Ports: Interfaces defining how the core communicates
- Driving Adapters: Things that call your application (HTTP, CLI, tests)
- Driven Adapters: Things your application calls (DB, external APIs)
Domain-Driven Design (DDD)
Strategic Patterns:
| Pattern | Purpose |
|---|---|
| Bounded Context | Define clear boundaries between domains |
| Ubiquitous Language | Shared vocabulary between devs and domain experts |
| Context Mapping | Define relationships between bounded contexts |
Tactical Patterns:
| Pattern | Purpose | Example |
|---|---|---|
| Entity | Object with identity | User, Order |
| Value Object | Immutable, no identity | Money, Address |
| Aggregate | Cluster of entities | Order + OrderItems |
| Repository | Abstraction for persistence | UserRepository |
| Domain Event | Something that happened | OrderPlaced |
| Domain Service | Logic not belonging to entity | PaymentProcessor |
Distributed System Patterns
Microservices
When to Use:
- Large teams (> 10 developers)
- Need independent deployability
- Different scaling requirements per service
- Polyglot persistence needed
When to Avoid:
- Small teams
- Simple domains
- Tight latency requirements
- Limited DevOps capability
Event-Driven Architecture
┌─────────┐ ┌──────────────┐ ┌─────────┐
│ Service │────▶│ Event Broker │────▶│ Service │
│ A │ │ (Kafka/SQS) │ │ B │
└─────────┘ └──────────────┘ └─────────┘
│ │
└──────────── Events ────────────────┘
Patterns:
- Event Sourcing: Store all changes as events
- CQRS: Separate read and write models
- Saga: Manage distributed transactions
API Gateway Pattern
┌─────────┐ ┌─────────────┐ ┌───────────┐
│ Client │────▶│ API Gateway │────▶│ Service A │
└─────────┘ └──────┬──────┘ ├───────────┤
│ │ Service B │
└───────────▶├───────────┤
│ Service C │
└───────────┘
Gateway Responsibilities:
- Authentication/Authorization
- Rate limiting
- Request routing
- Load balancing
- Response caching
Resilience Patterns
Circuit Breaker
enum CircuitState {
CLOSED, // Normal operation
OPEN, // Failing, reject calls
HALF_OPEN // Testing recovery
}
class CircuitBreaker {
private state = CircuitState.CLOSED;
private failures = 0;
private threshold = 5;
async execute<T>(fn: () => Promise<T>): Promise<T> {
if (this.state === CircuitState.OPEN) {
throw new CircuitOpenError();
}
try {
const result = await fn();
this.onSuccess();
return result;
} catch (error) {
this.onFailure();
throw error;
}
}
}
Retry with Exponential Backoff
async function retryWithBackoff<T>(
fn: () => Promise<T>,
maxRetries: number = 3
): Promise<T> {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (i === maxRetries - 1) throw error;
await sleep(Math.pow(2, i) * 1000); // 1s, 2s, 4s
}
}
}
Bulkhead
Isolate failures to prevent cascade:
class Bulkhead {
private semaphore: Semaphore;
constructor(maxConcurrent: number) {
this.semaphore = new Semaphore(maxConcurrent);
}
async execute<T>(fn: () => Promise<T>): Promise<T> {
await this.semaphore.acquire();
try {
return await fn();
} finally {
this.semaphore.release();
}
}
}
Data Patterns
Repository Pattern
interface UserRepository {
findById(id: string): Promise<User | null>;
findByEmail(email: string): Promise<User | null>;
save(user: User): Promise<void>;
delete(id: string): Promise<void>;
}
// Implementation details hidden
class PostgresUserRepository implements UserRepository {
// ...database-specific code
}
Unit of Work
interface UnitOfWork {
users: UserRepository;
orders: OrderRepository;
commit(): Promise<void>;
rollback(): Promise<void>;
}
CQRS (Command Query Responsibility Segregation)
┌──────────────┐ ┌──────────────────┐
│ Commands │────────▶│ Write Model │
│ (Create, │ │ (Normalized) │
│ Update) │ └────────┬─────────┘
└──────────────┘ │
│ Sync
▼
┌──────────────┐ ┌──────────────────┐
│ Queries │────────▶│ Read Model │
│ (GetById, │ │ (Denormalized) │
│ Search) │ └──────────────────┘
└──────────────┘
When to Use What
| Scenario | Recommended Pattern |
|---|---|
| Complex domain logic | DDD + Clean Architecture |
| High scalability | Microservices + Event-Driven |
| Simple CRUD | MVC / Layered Architecture |
| Real-time updates | Event Sourcing + CQRS |
| Legacy migration | Strangler Fig Pattern |
| API aggregation | BFF (Backend for Frontend) |
Anti-Patterns to Avoid
| Anti-Pattern | Problem | Solution |
|---|---|---|
| Big Ball of Mud | No structure | Introduce bounded contexts |
| Distributed Monolith | Coupled services | True service boundaries |
| Anemic Domain Model | Logic in services | Rich domain model |
| Golden Hammer | One pattern for all | Right tool for job |
| Premature Abstraction | Over-engineering | YAGNI, iterate |
Decision Framework
Before choosing an architecture:
What are the scaling requirements?
- Users, requests, data volume
What is the team size/structure?
- Small team → simpler architecture
- Multiple teams → clear boundaries
What are the consistency requirements?
- Strong consistency → synchronous
- Eventual consistency → event-driven
What is the expected rate of change?
- High change → modular, loosely coupled
- Stable → simpler is better
What are the operational capabilities?
- Limited DevOps → monolith
- Strong DevOps → microservices possible