Files
gridpilot.gg/docs/architecture/CQRS.md
2025-12-20 12:22:48 +01:00

244 lines
4.8 KiB
Markdown

CQRS Light with Clean Architecture
This document defines CQRS Light as a pragmatic, production-ready approach that integrates cleanly with Clean Architecture.
It is intentionally non-dogmatic, avoids event-sourcing overhead, and focuses on clarity, performance, and maintainability.
1. What CQRS Light Is
CQRS Light separates how the system writes data from how it reads data — without changing the core architecture.
Key properties:
• Commands and Queries are separated logically, not infrastructurally
• Same database is allowed
• No event bus required
• No eventual consistency by default
CQRS Light is an optimization, not a foundation.
2. What CQRS Light Is NOT
CQRS Light explicitly does not include:
• Event Sourcing
• Message brokers
• Projections as a hard requirement
• Separate databases
• Microservices
Those can be added later if needed.
3. Why CQRS Light Exists
Without CQRS:
• Reads are forced through domain aggregates
• Aggregates grow unnaturally large
• Reporting logic pollutes the domain
• Performance degrades due to object loading
CQRS Light solves this by allowing:
• Strict domain logic on writes
• Flexible, optimized reads
4. Core Architectural Principle
Writes protect invariants. Reads optimize information access.
Therefore:
• Commands enforce business rules
• Queries are allowed to be pragmatic and denormalized
5. Placement in Clean Architecture
CQRS Light does not introduce new layers.
It reorganizes existing ones.
core/
└── <context>/
└── application/
├── commands/ # Write side (Use Cases)
└── queries/ # Read side (Query Use Cases)
Domain remains unchanged.
6. Command Side (Write Model)
Purpose
• Modify state
• Enforce invariants
• Emit outcomes
Characteristics
• Uses Domain Entities and Value Objects
• Uses Repositories
• Uses Output Ports
• Transactional
Example Structure
core/racing/application/commands/
├── CreateLeagueUseCase.ts
├── ApplyPenaltyUseCase.ts
└── RegisterForRaceUseCase.ts
7. Query Side (Read Model)
Purpose
• Read state
• Aggregate data
• Serve UI efficiently
Characteristics
• No domain entities
• No invariants
• No side effects
• May use SQL/ORM directly
Example Structure
core/racing/application/queries/
├── GetLeagueStandingsQuery.ts
├── GetDashboardOverviewQuery.ts
└── GetDriverStatsQuery.ts
Queries are still Use Cases, just read-only ones.
8. Repositories in CQRS Light
Write Repositories
• Used by command use cases
• Work with domain entities
• Enforce consistency
core/racing/domain/repositories/
└── LeagueRepositoryPort.ts
Read Repositories
• Used by query use cases
• Return flat, optimized data
• Not domain repositories
core/racing/application/ports/
└── LeagueStandingsReadPort.ts
Implementation lives in adapters.
9. Performance Benefits (Why It Matters)
Without CQRS Light
• Aggregate loading
• N+1 queries
• Heavy object graphs
• CPU and memory overhead
With CQRS Light
• Single optimized queries
• Minimal data transfer
• Database does aggregation
• Lower memory footprint
This results in:
• Faster endpoints
• Simpler code
• Easier scaling
10. Testing Strategy
Commands
• Unit tests
• Mock repositories and ports
• Verify output port calls
Queries
• Simple unit tests
• Input → Output verification
• No mocks beyond data source
CQRS Light reduces the need for complex integration tests.
11. When CQRS Light Is a Good Fit
Use CQRS Light when:
• Read complexity is high
• Write logic must stay strict
• Dashboards or analytics exist
• Multiple clients consume the system
Avoid CQRS Light when:
• Application is CRUD-only
• Data volume is small
• Read/write patterns are identical
12. Migration Path
CQRS Light allows incremental adoption:
1. Start with classic Clean Architecture
2. Separate commands and queries logically
3. Optimize read paths as needed
4. Introduce events or projections later (optional)
No rewrites required.
13. Key Rules (Non-Negotiable)
• Commands MUST use the domain
• Queries MUST NOT modify state
• Queries MUST NOT enforce invariants
• Domain MUST NOT depend on queries
• Core remains framework-agnostic
14. Mental Model
Think of CQRS Light as:
One core truth, two access patterns.
The domain defines truth.
Queries define convenience.
15. Final Summary
CQRS Light provides:
• Cleaner domain models
• Faster reads
• Reduced complexity
• Future scalability
Without:
• Infrastructure overhead
• Event sourcing complexity
• Premature optimization
It is the safest way to gain CQRS benefits while staying true to Clean Architecture.