Files
gridpilot.gg/docs/architecture/core/CQRS.md
2026-01-11 14:42:54 +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. Adoption Rule (Strict)

CQRS Light is a structural rule inside Core.

If CQRS Light is used:

  • commands and queries MUST be separated by responsibility
  • queries MUST remain read-only and must not enforce invariants

This document does not define a migration plan.

  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.