Skip to content

Implement CQRS in PHP. The Right Way.

Implementing Command Query Responsibility Segregation correctly means weeks of manual infrastructure work. Jardis generates commands, queries, handlers, and response objects as separate, typed DTOs, structurally enforced instead of by convention.

Everyone wants CQRS. Almost nobody implements it correctly in PHP.

The pattern sounds simple. Consistent, long-term implementation in real projects is not.

Read and write paths blend together

It starts with a query that writes data on the side. Or a command that returns a result. A few sprints later, the separation is purely theoretical. Repositories serve both read and write, and there are no DTOs.

Every handler looks different

Every developer implements commands and queries differently. Sometimes with a dedicated handler, sometimes inline in the controller. Sometimes with a response object, sometimes returning an array. No uniform pattern, no predictability.

Weeks spent on infrastructure

Before the team writes the first line of business logic, weeks pass with command bus setup, query bus configuration, DTO base classes, and handler registration. Recurring infrastructure work that looks identical in every PHP project and still happens manually every time.

How Jardis Implements CQRS.

Not as an optional convention, but as physical separation in dedicated namespaces.

COMMAND PIPELINE

Typed commands with dedicated handlers

Jardis generates a CommandDTO and a dedicated CommandHandler for every write operation. Commands live in their own namespace within the Bounded Context. No mixing with queries, no generic handlers, no shortcuts.

QUERY PIPELINE

The read side as its own path

Every query gets a QueryDTO, a QueryHandler, and a typed response object. The 5-stage repository pipeline separates read and write paths at the repository level. Queries only access read repositories, commands only access write repositories.

STRUCTURAL SEPARATION

CQRS PHP implementation without erosion

Commands and queries live in separate namespaces within every Bounded Context. This separation is not a guideline you can ignore. It is the folder structure itself. New team members see immediately what is a command and what is a query.

See what three files turn into.

Three definition files in, a complete bounded context out. Browse the generated code.

E-Commerce / Sales
schema.yaml
# Database Schema — Sales Bounded Context
# This file defines the persistent storage structure.

schema:
  domain: ECommerce
  boundedContext: Sales

tables:
  order:
    columns:
      id:
        type: integer
        primary: true
        autoIncrement: true
      public_id:
        type: uuid7
        unique: true
      customer_email:
        type: string
        length: 255
      status:
        type: string
        length: 32
        default: "draft"
      total_amount:
        type: integer
      currency:
        type: string
        length: 3
        default: "EUR"
      created_at:
        type: datetime
      updated_at:
        type: datetime
        nullable: true

  order_item:
    columns:
      id:
        type: integer
        primary: true
        autoIncrement: true
      order_id:
        type: integer
        foreignKey:
          table: order
          column: id
          onDelete: cascade
      product_name:
        type: string
        length: 255
      sku:
        type: string
        length: 64
      quantity:
        type: integer
      unit_price:
        type: integer
      line_total:
        type: integer
Files
Definitions (Input)
Generated Code (Output)
BUILDER OUTPUT
80%
CQRS infrastructure generatedCommandDTOs, QueryDTOs, handlers, response objects, read/write repository splitting. Your team only writes the business logic inside the handlers.
3x
faster start for new Bounded Contexts
0
mixing of read and write
TYPE SAFETY
100%
Typed DTOsEvery command, every query, every response is a typed PHP object. No associative arrays, no generic stdClass. Static analysis works from day one.

Why Teams Choose Jardis for CQRS.

Because CQRS does not fail because of understanding. It fails because of inconsistent implementation.

> Clear Responsibilities

Every operation has exactly one path

Commands write, queries read. No handler that does both. No repository that mixes read and write. The separation is not optional. It is the structure.

> Speed

CQRS infrastructure from the first builder run

Define the schema, start the builder. CommandDTOs, QueryDTOs, handlers, and response objects are ready instantly. The complete CQRS infrastructure for a new Bounded Context is production-ready. No manual bus configuration, no base class debates.

> Consistency

Commands and queries follow one standard

Whether three or thirty contexts: commands and queries follow the same structure. New team members understand one context and know them all. No special cases, no historically grown deviations.

CQRS as the standard, not as wishful thinking?

Join the Waitlist

Structure costs less than chaos.

Free Trial

Try Jardis 7 Days Free

Point Jardis at your real domain. Discovery, structure, and your first platform build.

Join Waitlist
20 Discovery Runs
5 Structure Builds
1 Platform Build
All Jardis packages as open source
Jardis Base
€29per month

The complete DDD architecture with all classes and contracts. Your team ships features, not infrastructure.

Join Waitlist
Unlimited Discovery Runs
Unlimited Structure Builds
All 26 Jardis packages included
PHPStan Level 8 from day one
Jardis Pro
€180per month

The complete business logic with handlers, validation, and pipelines. What used to be a sprint is now a build.

Join Waitlist
Everything from Jardis Base
Commands, queries, and events fully implemented
Platform code in seconds instead of weeks
Additional Runs for €89 each
Enterprise

More than 20 Platform Builds per month?

Let's talk

Be there when Jardis launches.

Sign up. You'll get access as soon as we go live. Including a free trial.

100+ developers are already waiting for launch

Curious how Jardis works?

Discover Jardis

Frequently Asked Questions

Answers about CQRS with Jardis.

Per Bounded Context, Jardis generates typed CommandDTOs with dedicated CommandHandlers and QueryDTOs with QueryHandlers and response objects in PHP. Commands and queries live in separate namespaces. The repository pipeline physically separates read and write paths, so queries can only reach read repositories.