agentleFS
AnyOrgNameAnyOrgName/php-skills/skills/architecture/cqrs/SKILL.md

cqrs

Skill4 starsChanged 4 months agoMITView on GitHub

Split the write side (commands that change state and return nothing) from the read side (queries that return data and change nothing), so each side gets a model shaped for its job instead of one model straining to do both. Use when the user wants to represent each state-changing intent as a command object dispatched to a dedicated handler; the user wants distinct models for changing state and for reading data because one shared model has become awkward for both; the user wants read models tailored to each view, bypassing the write model to return exactly what a screen needs. Keywords: cqrs, command, read-model, write-model, separation, handler.

3 other projects use a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

3 rules. About 0 are common, 3 are distinctive, and 0 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈3

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 0

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Do not adopt event sourcing or eventual consistency just because you are separating reads from writes.

    line 64
  2. Distinctive:

    Do not split reads and writes for a plain CRUD screen where one model serves both sides comfortably.

    line 65
  3. Distinctive:

    Not applicable — no I/O, auth, or untrusted input.

    line 123

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md
<!-- generated: do not edit — source: knowledge/atoms/architecture/cqrs/ --><!-- schema: atomVersion=3 skillVersion=2 graphVersion=1 --><!-- compile: @php-skills/compiler v1.0.0 (deterministic build) -->

CQRS (Command Query Responsibility Segregation)

When to use

  • The user wants to represent each state-changing intent as a command object dispatched to a dedicated handler.
  • The user wants distinct models for changing state and for reading data because one shared model has become awkward for both.
  • The user wants read models tailored to each view, bypassing the write model to return exactly what a screen needs.

When NOT to use

  • Do not adopt event sourcing or eventual consistency just because you are separating reads from writes.
  • Do not split reads and writes for a plain CRUD screen where one model serves both sides comfortably.

Core guidance

  • Make commands change state and return nothing — Let a command express an intent to change state and return void (or only an identifier); never return the queried result of the change from the command handler.
  • Make queries return data with no side effects — Let a query return data and change nothing observable; a query handler must never write to the store or mutate domain state.

Quick recipe

  1. Define an immutable command carrying the intent's data (e.g. final class RenameCustomer) and a handler whose handle() applies the change and returns void. (verify: The handler mutates state and returns nothing beyond an optional new id)
  2. Define a query (e.g. final class CustomerProfile) and a handler returning a view-shaped read model DTO with no side effects. (verify: The query handler only reads and returns data the screen needs)
  3. Route commands and queries through a small dispatcher (or the framework's message bus) that maps each message to its single handler, registered at the composition root. (verify: Dispatching a command invokes only its handler; dispatching a query returns its read model)

Best practices

  • Route each command or query message to exactly one handler dedicated to it, rather than a service with many mixed methods
  • Build read models as flat, view-shaped DTOs that a query returns directly, rather than loading the write-side aggregate and reshaping it in the controller
  • Separate the command and query models in code first while both read from the same database; only introduce a separate read store when a measured need justifies it

Anti-patterns

Anti-patternWhy it failsDo instead
A command that also queries and rendersA handler that both writes and reads recreates the mixed model CQRS removes, so the two responsibilities re-entangle and testing gets harder.Keep the command handler to applying the change, and put any read shaping in a separate query<br>handler the caller invokes next.
A query handler that writes to the storeA read that secretly writes breaks the guarantee that queries are safe to cache and retry, and hides state changes on the read path.Move any needed write into an explicit command, and keep the query handler purely returning<br>data.
Naming it CQRS but sharing one modelIf both sides depend on the same mutable model, nothing is actually segregated and the pattern's clarity and independence are absent.Give the write side its aggregate and the read side its own view-shaped model, even when they<br>read from the same database today.

See references/anti-patterns.md.

Common mistakes

  1. A command handler that returns the read result — Returning the full updated entity from a command handler so the caller can render it merges the write and read responsibilities the split was meant to keep apart. Fix: Return at most a new identifier from the command, then issue a separate query to fetch the read model for display.
  2. Surprising staleness from an async read model — Updating the read model asynchronously and then reading it immediately after a command can show stale data, confusing users who expect their change to appear at once. Fix: Keep the read model synchronous until you truly need async, and where it is async, signal the pending state in the UI or read from the write side for the just-changed record.
  3. Applying CQRS to every corner of the app — Forcing commands, queries, handlers, and read models onto simple lookups and settings screens buries trivial features under ceremony. Fix: Apply CQRS to the bounded contexts with real read/write asymmetry, and let simple areas keep a plain shared model.

Version notes

  • Immutable messages with PHP 8.1 readonly (PHP >=8.1) — PHP 8.1 readonly promoted properties let commands and queries be defined as immutable data carriers in a couple of lines, reinforcing that a message is a fixed request.
  • Typed class constants aid message metadata in PHP 8.3 (PHP >=8.3) — From PHP 8.3, typed class constants let a command or read model declare stable metadata (a message name or version) with an enforced type on the message class.

Security

  • Not applicable — no I/O, auth, or untrusted input.

Testing

  • Activation and decision behavior are verified in eval/scenarios/architecture/cqrs.yaml.

Additional resources

  • references/anti-patterns.md
  • references/core.md
  • references/examples.md
  • references/pitfalls.md
  • references/recipes.md
  • references/version-differences.md

Knowledge graph

Related: architecture/hexagonal, architecture/service-layer

<!-- graph-fragment: graph/fragments/architecture/cqrs.yaml -->

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.