Files
gridpilot.gg/docs/architecture/core/CQRS.md
2026-01-11 13:04:33 +01:00

4.8 KiB

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.

  1. 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.

  1. 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

  1. Core Architectural Principle

Writes protect invariants. Reads optimize information access.

Therefore: • Commands enforce business rules • Queries are allowed to be pragmatic and denormalized

  1. Placement in Clean Architecture

CQRS Light does not introduce new layers. It reorganizes existing ones.

core/ └── / └── application/ ├── commands/ # Write side (Use Cases) └── queries/ # Read side (Query Use Cases)

Domain remains unchanged.

  1. 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

  1. 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.

  1. 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.

  1. 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

  1. 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.

  1. 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

  1. 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.

  1. 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

  1. Mental Model

Think of CQRS Light as:

One core truth, two access patterns.

The domain defines truth. Queries define convenience.

  1. 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.