🔥 0
0
Lesson 1 of 6 15 min +150 XP

What is CQRS?

Your e-commerce platform is struggling. Writes to the order system need strong consistency and complex validation. Reads for the product catalog need to be blazing fast with denormalized data for search. You're trying to serve both needs with the same data model, and it's not working.

CQRS solves this by separating reads from writes entirely.

The Core Idea

Command Query Responsibility Segregation means exactly what it says:

COMMANDS (Writes)

  • Change system state
  • CreateOrder, UpdateProfile
  • Can fail validation
  • Return success/failure only
  • Optimized for consistency

QUERIES (Reads)

  • Return data, no side effects
  • GetOrderDetails, SearchProducts
  • Never modify state
  • Return rich view models
  • Optimized for performance

Traditional vs CQRS Architecture

Traditional: Single Model

┌─────────────────────────────────────────┐
│              Application                │
│  ┌─────────────────────────────────┐   │
│  │         Single Model            │   │
│  │   (handles both R & W)          │   │
│  └─────────────────────────────────┘   │
│                  │                      │
│                  ▼                      │
│  ┌─────────────────────────────────┐   │
│  │         Single Database         │   │
│  └─────────────────────────────────┘   │
└─────────────────────────────────────────┘

CQRS: Separated Models

┌─────────────────────────────────────────────────────┐
│                    Application                      │
│                                                     │
│  ┌──────────────────┐    ┌──────────────────┐      │
│  │   Write Model    │    │    Read Model    │      │
│  │   (Commands)     │    │    (Queries)     │      │
│  └────────┬─────────┘    └────────┬─────────┘      │
│           │                       │                 │
│           ▼                       ▼                 │
│  ┌──────────────────┐    ┌──────────────────┐      │
│  │   Write Store    │───▶│   Read Store     │      │
│  │  (Normalized)    │sync│  (Denormalized)  │      │
│  └──────────────────┘    └──────────────────┘      │
└─────────────────────────────────────────────────────┘

Why Separate Reads and Writes?

📊
Different Scaling

Reads often 100x more than writes - scale independently

🎯
Optimized Models

Writes: normalized, validated. Reads: denormalized, fast

🔒
Security

Easier to audit and secure write paths separately

🧩
Complexity

Complex domains have complex write logic but simple reads

A Simple Example: E-Commerce Order

Without CQRS (Traditional)

// One model tries to do everything
class Order {
  id: string;
  customerId: string;
  items: OrderItem[];
  status: OrderStatus;
  shippingAddress: Address;
  billingAddress: Address;
  // ... 20 more fields for various use cases

  // Write logic mixed with read concerns
  addItem(item: OrderItem) { /* validation, business rules */ }

  // Complex joins needed for display
  getOrderSummary() { /* joins with products, customer, shipping */ }
}

With CQRS

// WRITE SIDE: Focus on business rules and validation
class PlaceOrderCommand {
  customerId: string;
  items: { productId: string; quantity: number }[];
  shippingAddressId: string;
}

class OrderCommandHandler {
  handle(cmd: PlaceOrderCommand) {
    // Validate inventory
    // Check customer credit
    // Apply business rules
    // Emit OrderPlaced event
  }
}

// READ SIDE: Optimized for display
interface OrderSummaryReadModel {
  orderId: string;
  customerName: string;           // Denormalized
  totalAmount: number;            // Pre-calculated
  itemCount: number;              // Pre-calculated
  statusDisplay: string;          // Human-readable
  estimatedDelivery: Date;        // Pre-computed
}

class OrderQueryService {
  getOrderSummary(orderId: string): OrderSummaryReadModel {
    // Simple, fast lookup from read-optimized store
    return this.readDb.findById(orderId);
  }
}

Real-World: LinkedIn's Activity Feed

LinkedIn's Feed Architecture

LinkedIn uses CQRS for their activity feed. When you post an update, it goes through complex processing (spam detection, relevance scoring, compliance checks). But when you view your feed, it's a simple read from pre-computed, personalized data stores optimized for fast retrieval.

Writes: Complex validation pipeline | Reads: Sub-100ms feed retrieval

When to Use CQRS

Use CQRS When Avoid CQRS When
Read/write scaling needs differ significantly Simple CRUD with equal read/write patterns
Complex business logic on writes Straightforward validation rules
Read model differs greatly from write model One model fits all use cases
Team can handle eventual consistency Strong consistency is mandatory everywhere

The Trade-offs

CQRS Adds Complexity

CQRS introduces eventual consistency between read and write stores. After a command succeeds, reads might not immediately reflect the change. Your UI and business processes need to handle this gracefully.

Benefits:
  • Independent scaling of reads vs writes
  • Optimized data models for each purpose
  • Cleaner separation of concerns
  • Better performance for read-heavy workloads
Costs:
  • Eventual consistency complexity
  • More infrastructure to manage
  • Data synchronization logic
  • Steeper learning curve

Key Takeaways

  • CQRS separates reads from writes - Different models, different stores, different scaling
  • Commands change state - They carry intent, go through validation, and may fail
  • Queries return data - Fast, denormalized, no side effects
  • Not always needed - Use CQRS when complexity justifies the overhead

Next up: Event Sourcing Basics - Learning how events become your single source of truth.

🧠 Quick Quiz

Test your understanding of this lesson.

1

What does CQRS stand for?

2

In CQRS, what is a 'Command'?

3

When is CQRS most beneficial?