opencode-workflow/skills/architecture-patterns/SKILL.md

182 lines
7.2 KiB
Markdown
Raw Normal View History

2026-04-10 09:24:59 +00:00
---
name: architecture-patterns
description: "Knowledge contract for selecting architectural patterns based on requirements. Covers modular monolith, microservices, layered, clean, hexagonal, event-driven, CQRS, saga, and outbox patterns. Referenced by design-architecture when selecting patterns."
---
This is a knowledge contract, not a workflow skill. It is referenced by `design-architecture` when the architect is selecting architectural patterns.
## Core Principle
Choose patterns only when they solve a real problem identified in the PRD. Do not apply patterns because they are fashionable, because other projects use them, or because they might be needed someday.
Every pattern choice must be traced to a specific PRD requirement or NFR. If no PRD requirement justifies a pattern, do not use it.
## Pattern Catalog
### Modular Monolith
One deployment unit with well-defined internal modules.
Use when:
- Domain boundaries are still evolving
- Team is small (fewer than 5-8 engineers per boundary)
- Deployment simplicity is a priority
- The PRD does not require independent service scaling
- You need the flexibility to split later when boundaries stabilize
Avoid when:
- Individual modules have vastly different scaling requirements
- Independent deployment is a hard requirement
- Teams need to own and deploy modules independently
Trade-offs: +simplicity, +single deployment, +easy refactoring, -scaling granularity, -independent deployability
### Microservices
Multiple independently deployable services, each with a single responsibility.
Use when:
- Individual services have different scaling requirements
- Domain boundaries are stable and well-understood
- Independent deployment of services is required
- The PRD requires isolation for reliability or security
- Teams need to own services end-to-end
Avoid when:
- Domain boundaries are not yet clear
- Team size does not support operational overhead
- Inter-service communication overhead is unjustified
- The PRD does not require independent scaling or deployment
Trade-offs: +independent deployment, +scaling granularity, +fault isolation, -operational complexity, -network overhead, -distributed data challenges
### Layered Architecture
Organize code into horizontal layers (presentation, business, data).
Use when:
- The application is straightforward CRUD or simple business logic
- The team is familiar with this pattern
- There is no need for complex domain modeling
Avoid when:
- Business logic is complex and needs to be isolated from infrastructure
- The application has varying persistence requirements
- You need to swap infrastructure implementations
Trade-offs: +simplicity, +familiarity, -tight coupling to infrastructure, -harder to test business logic in isolation
### Clean Architecture
Organize code around use cases with dependency inversion, keeping business logic independent of frameworks and infrastructure.
Use when:
- Business logic is complex and must be protected from infrastructure changes
- The application has multiple delivery mechanisms (API, CLI, web, mobile)
- Testability is a top priority
- Long-term maintainability is critical
Avoid when:
- The application is simple CRUD with minimal business logic
- The team is small and infrastructure changes are unlikely
- Overhead of indirection outweighs maintainability benefit
Trade-offs: +testability, +independence from frameworks, +long-term maintainability, -indirection, -more files and interfaces
### Hexagonal Architecture (Ports & Adapters)
Isolate business logic from external concerns through ports (interfaces) and adapters (implementations).
Use when:
- You need to swap external dependencies (databases, APIs, message queues)
- You want to test business logic without external infrastructure
- The application may have multiple input/output channels
Avoid when:
- The application has a single, stable external dependency
- The indirection overhead is not justified by the project scale
Trade-offs: +testability, +flexibility, +swap ability, -indirection, -interface overhead
### Event-Driven Architecture
Components communicate through events rather than direct calls.
Use when:
- The PRD requires loose coupling between components
- Multiple consumers need to react to the same event
- Async processing is required (see `async-queue-design`)
- Cross-service consistency is eventual (see `distributed-system-basics`)
Avoid when:
- The PRD requires strong consistency across services
- The system is simple enough for direct calls
- Event traceability and debugging overhead is not justified
- The team lacks event-driven experience and the timeline is tight
Trade-offs: +loose coupling, +scalability, +reactive, -debugging complexity, -eventual consistency, -ordering challenges
### CQRS (Command Query Responsibility Segregation)
Separate read models from write models.
Use when:
- Read and write patterns are vastly different (read-heavy, complex queries vs simple writes)
- Read models need to be optimized differently from write models
- The PRD requires different consistency or scaling for reads vs writes
Avoid when:
- Read and write patterns are similar
- The added complexity of sync between models is not justified
- The system is small enough that a single model suffices
Trade-offs: +read optimization, +scaling, +query flexibility, -complexity, -eventual consistency between models, -sync logic
### Saga Pattern
Manage distributed transactions across services using a sequence of local transactions with compensating actions.
Use when:
- A business process spans multiple services
- Each service owns its own data and cannot participate in a distributed transaction
- The PRD requires atomicity across service boundaries
Avoid when:
- The process fits within a single service
- The PRD does not require cross-service atomicity
- Compensating transactions are hard to define (irreversible operations like sending email)
Trade-offs: +cross-service consistency, +service autonomy, -complexity, -compensation logic, -debugging difficulty
### Outbox Pattern
Ensure reliable event publishing by writing events to an outbox table in the same transaction as the data change.
Use when:
- You need to publish events reliably when data changes
- The message queue or event broker might be temporarily unavailable
- At-least-once delivery is required but the system cannot lose events
Avoid when:
- Event loss is acceptable
- The system does not publish events based on data changes
- The added database write overhead is not justified
Trade-offs: +reliability, +exactly-once processing (with idempotency), -write overhead, -outbox polling or CDC complexity
## Pattern Selection Process
1. Identify the specific PRD requirement or NFR that motivates a pattern
2. List 2-3 candidate patterns that could address the requirement
3. Evaluate each against the project context (team size, timeline, complexity tolerance)
4. Select the simplest pattern that satisfies the requirement
5. Document the decision as an ADR (refer to design-architecture template)
## Anti-Patterns
- Applying CQRS to a simple CRUD application
- Using microservices when boundaries are unclear
- Using sagas for single-service transactions
- Adding event-driven architecture for 1-to-1 communication
- Applying clean architecture to a throwaway prototype
- Choosing patterns based on resume appeal rather than requirements