182 lines
7.2 KiB
Markdown
182 lines
7.2 KiB
Markdown
|
|
---
|
||
|
|
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
|