← Home ← Codex ← DEBT ← Engine
Browse by Category
+ added · updated 7d
← Back to glossary

CQRS Pattern

Messaging PHP 7.0+ Advanced
debt(d7/e7/b7/t7)
d7 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'only careful code review or runtime testing' (d7). CQRS is an architectural pattern — there are no static analysis tools that can detect whether you've over-applied CQRS, returned data from command handlers inappropriately, or prematurely adopted full event sourcing. These issues only surface during code review, architectural discussions, or when performance/complexity problems manifest at runtime. No detection_hints.tools are specified for this term.

e7 Effort Remediation debt — work required to fix once spotted

Closest to 'cross-cutting refactor across the codebase' (e7). While the quick_fix suggests starting simple with separate ReadRepository/WriteRepository classes, fixing CQRS misapplication (such as having applied it everywhere, or having committed to full event sourcing prematurely) requires significant architectural rework. Untangling command handlers, query handlers, event stores, and read model projections across the codebase is a major undertaking that touches many components and requires careful migration.

b7 Burden Structural debt — long-term weight of choosing wrong

Closest to 'strong gravitational pull' (b7). CQRS fundamentally shapes how code is organised — every feature must be split into commands and queries, handlers must be created, and the team must maintain discipline around the separation. The applies_to shows it affects both web and cli contexts. Once adopted, every new feature is shaped by this pattern. The common_mistakes note that applying CQRS everywhere adds complexity, and the misconception warns that full CQRS+ES defines the system's data architecture.

t7 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'serious trap' (t7). The misconception field explicitly states developers believe 'CQRS requires event sourcing and a message bus' when in reality it exists on a spectrum starting with simple class separation. This contradicts how the pattern is commonly presented in tutorials and conference talks. The common_mistakes reinforce traps: returning data from commands (seems natural, is wrong), synchronous consistency when eventual is intended, and jumping to full event sourcing prematurely. These are not edge cases but fundamental misunderstandings.

About DEBT scoring →

Also Known As

Command Query Responsibility Segregation Command Bus Pattern Read-Write Separation

TL;DR

Command Query Responsibility Segregation — separating the write model (commands that change state) from the read model (queries that return data), allowing each to be optimised independently.

Explanation

CQRS separates an application into two distinct sides: the command side handles write operations (create, update, delete) through command objects processed by command handlers, updating a normalised write model; the query side handles read operations through query objects processed by query handlers, reading from a denormalised read model optimised for display. The read model can be a database view, a cached projection, a separate database, or even a different storage technology than the write model. In its simplest form, CQRS in PHP is just a coding pattern — separate service classes for reads and writes, no shared models. In advanced implementations, the read model is derived from events emitted by the write model (event sourcing), enabling separate scaling, separate databases, and rebuilding the read model from scratch by replaying events. Laravel has several CQRS libraries (Tactician, Broadway); Symfony provides the Messenger component which fits the command bus pattern.

Common Misconception

CQRS requires event sourcing and a message bus. CQRS exists on a spectrum. At the simplest level, it means using separate service classes for reads and writes — no event sourcing, no message bus, just a naming and organisational pattern. The full CQRS + Event Sourcing architecture with separate databases is one end of the spectrum, not the entry point. Start with logical separation in code and add infrastructure complexity only when the read/write scaling or audit requirements justify it.

Why It Matters

CQRS solves the tension between read and write models that grows in complex PHP applications. The write model should be normalised to prevent anomalies; the read model should be denormalised to avoid expensive joins in display queries. In a traditional architecture, these conflicting requirements produce either a normalised database with slow read queries, or a denormalised database with complex write logic. CQRS allows both: a normalised write model for consistency, and materialised read models optimised for each view. The separation also makes code significantly easier to test — command handlers and query handlers have single responsibilities.

Common Mistakes

  • Applying CQRS to every part of the application — it adds complexity and is only justified where read/write models genuinely diverge or where separate scaling is needed.
  • Returning data from command handlers — commands should return void or a command ID; read the updated state with a query if needed.
  • Keeping the read and write models in sync synchronously when they are meant to be eventually consistent — this negates the performance benefit of separate models.
  • Starting with the full event sourcing implementation before validating that the simpler CQRS separation solves the problem — YAGNI applies.

Avoid When

  • Simple CRUD applications with straightforward read/write patterns where the added complexity of separate models provides no performance or scalability benefit.
  • Teams unfamiliar with event-driven architecture or asynchronous messaging, as CQRS introduces operational complexity that requires solid infrastructure and debugging practices.
  • Systems where read and write models must stay perfectly consistent in real-time, since eventual consistency between separated models is often difficult to eliminate.
  • Projects with tight deadlines or limited budget, as the initial overhead of implementing dual models typically delays feature delivery compared to a unified model approach.

When To Use

  • Your read and write loads are asymmetrical — queries vastly outnumber writes or require different optimization strategies (caching, denormalisation, separate scaling).
  • You need an audit trail or event history of state changes — event sourcing with CQRS gives you a complete record and the ability to rebuild read models.
  • Your read model requires a shape different from your write model — for example, writes normalise data into relational tables but reads need a flat, denormalised view for performance.
  • You're building a system where multiple services or teams need to consume the same data in different shapes — CQRS lets each service maintain its own optimised read projection.

Code Examples

✗ Vulnerable
// Mixed read/write in one service — tight coupling
class OrderService {
    public function createOrder(array $data): Order {
        $order = Order::create($data);
        // Read mixed with write
        return Order::with(['items', 'user', 'address'])
            ->find($order->id); // N+1, heavy join for a write operation
    }
}
✓ Fixed
// Separate command and query handlers
class CreateOrderHandler {
    public function handle(CreateOrder $cmd): string {
        $order = new Order($cmd->customerId, $cmd->items);
        $this->orders->save($order);
        $this->eventBus->publish(new OrderCreated($order->id));
        return $order->id; // return ID only, not full read model
    }
}

class OrderSummaryQuery {
    public function findById(string $id): OrderSummaryDTO {
        // Reads from denormalised read model — fast, no joins
        return $this->readDb->query(
            'SELECT * FROM order_summaries WHERE id = ?', [$id]
        );
    }
}

Added 23 Mar 2026
Edited 22 Sep 2026
Views 119
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings W 0 pings T 0 pings F 0 pings S 1 ping S 0 pings M 1 ping T 0 pings W 0 pings T 1 ping F 2 pings S 0 pings S 1 ping M 2 pings T 0 pings W 0 pings T 0 pings F 0 pings S 0 pings S 1 ping M 1 ping T 0 pings W 0 pings T 0 pings F 1 ping S 1 ping S 1 ping M 0 pings T 0 pings W 0 pings T
No pings yet today
No pings yesterday
Amazonbot 14 PetalBot 10 Ahrefs 8 Google 6 Bing 6 Perplexity 5 Scrapy 4 SEMrush 4 Applebot 2 ChatGPT 1 Meta AI 1 Majestic 1 Brave Search 1 Twitter/X 1 Baidu 1
crawler 64 crawler_json 1
DEV INTEL Tools & Severity
🔵 Info ⚙ Fix effort: High
⚡ Quick Fix
Start with separate ReadRepository and WriteRepository classes sharing the same database — no infrastructure change, just clear separation. Add separate storage or event sourcing only when required
📦 Applies To
PHP 7.0+ web cli
🔗 Prerequisites


✓ schema.org compliant