CQRS and Event Sourcing in PHP: Architecture for Complex Projects

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Why Regular CRUD Stops Working as Your Project Grows



Imagine: you are writing an online store in PHP. At first, everything is simple — add a product, change the price, delete it. But six months pass, and new requirements appear: auditing all changes, restoring data to a specific date, complex discount logic. Regular CRUD (Create, Read, Update, Delete) starts to fall apart at the seams. The database turns into a mess, and each new client request requires rewriting half the system.



Sound familiar? Then you will definitely benefit from CQRS (Command Query Responsibility Segregation) and Event Sourcing — two powerful patterns that change the approach to development. In this article, we will break them down using real PHP examples so you can apply them in your projects.



What CQRS and Event Sourcing Are in Simple Terms



CQRS: Separating Commands and Queries



CQRS is an architectural pattern that separates operations into two types: commands (change state) and queries (read state). In classic MVC, both the controller and model work with the same entities. CQRS proposes to separate these responsibilities.



Imagine a library: there is a separate desk for writing books (commands) and a separate desk for reading (queries). No one interferes with each other, and each is optimized for its own task.



Event Sourcing: A History of Changes Instead of the Current State



Event Sourcing is a way of storing data where you save not the current state of an object, but the sequence of all events that led to it. Instead of one row "Order No. 123: status = delivered" you store: "Order created → Paid → Shipped → Delivered".



It's like a video recording instead of a photo: you can always rewind and see what happened at any moment.



When to Apply CQRS and Event Sourcing in PHP



These patterns are not a silver bullet. For a simple blog, they are overkill. But here are scenarios where they are indispensable:



  • Complex domain logic (finance, booking, logistics)
  • Audit requirements — you need to know who changed data and when
  • State restoration to any date
  • Microservice architecture — events are easily transferred between services
  • High read load — you can create a separate read model optimized for queries


If your project is an online store with thousands of orders, a CRM system, or a banking application — CQRS and Event Sourcing will definitely come in handy.



Practical Implementation of CQRS in PHP



Let's move on to the code. We will implement a simple order management system. Let's start with CQRS.



Step 1: Define Commands and Queries



A command is a DTO (Data Transfer Object) that contains all the data for performing an operation. A command never returns data — it only changes state.



<?php// src/Application/Command/CreateOrderCommand.phpnamespace App\\Application\\Command;

class CreateOrderCommand{ public function __construct( public readonly string $orderId, public readonly string $customerId, public readonly array $items, // [['productId' => '...', 'quantity' => 2], ...] public readonly \\DateTimeImmutable $createdAt ) {}}


A query is also a DTO, but for reading. It can contain filters and return data.



<?php// src/Application/Query/GetOrderQuery.phpnamespace App\\Application\\Query;

class GetOrderQuery{ public function __construct( public readonly string $orderId ) {}}


Step 2: Create Command Handlers



A command handler is a service that executes business logic. It receives a command and changes the state

Blogs

Book Recommendations