Skip to content
Back to skills

Cqrs

ASecurity

Command Query Responsibility Segregation patterns for separating write and read models.

  • 3 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 9, 2026
ai-agentstypescriptgoapibackend

Works with

  • api

Security analysis

A100/100

Scanned October 9, 2026

npx -y skills add alisattorov06/ali-kiro --skill cqrs --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Cqrs?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Cqrs
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/alisattorov06-cqrs/badge)](https://www.skillsdirectory.com/skills/alisattorov06-cqrs)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: cqrs
description: Command Query Responsibility Segregation patterns for separating write and read models.
origin: FlowDeck
---

# CQRS (Command Query Responsibility Segregation)

## When to Activate

Activate when:
- Designing read-heavy or write-heavy systems separately
- Implementing complex domain models with divergent read/write logic
- Building systems that need different data representations for reading vs. writing
- Scaling read and write workloads independently
- Implementing event sourcing alongside specialized read models

## Steps

### 1. Separate Command and Query Models

| Aspect | Command | Query |
|--------|---------|-------|
| Purpose | Modify state | Read state |
| Returns | Void / ACK | Data |
| Side Effects | Yes | No |
| Complexity | Business logic | Data shaping |

Commands and queries should use **different models** with different schemas optimized for their specific use case.

### 2. Design Command Side

- Commands are **intent-based** (present tense: `PlaceOrder`, `UpdatePrice`)
- Validate business rules **before** executing
- Return success/failure, not data
- Keep command handlers small and focused

```typescript
interface Command {
  id: string;          // Correlation ID
  type: string;        // Command type
  payload: unknown;    // Command data
  metadata: {
    userId: string;
    timestamp: string;
    correlationId: string;
  };
}

interface CommandHandler<T extends Command> {
  execute(command: T): Promise<CommandResult>;
}
```

### 3. Design Query Side

- Queries are **data-focused** (past/read tense: `GetUserOrders`, `FindActiveProducts`)
- Queries should be **side-effect free**
- Return **read-optimized** data structures (possibly denormalized)
- Support pagination, filtering, sorting

```typescript
interface Query {
  id: string;
  type: string;
  parameters: Record<string, unknown>;
  pagination?: { page: number; limit: number };
  sorting?: { field: string; direction: 'asc' | 'desc' }[];
}

interface QueryHandler<T extends Query> {
  execute(query: T): Promise<QueryResult>;
}
```

### 4. Implement Synchronization

When commands and queries share data:

1. **Synchronous** (same DB): Update the read model transactionally
2. **Asynchronous** (event-driven): Project events to read models
3. **Dual writes**: Update both models, handle eventual consistency

```typescript
// Synchronous synchronization
async function placeOrder(command: PlaceOrderCommand): Promise<void> {
  const order = Order.create(command.payload);

  await this.transactionManager.execute(async (tx) => {
    // Write to command model
    await this.orderRepo.save(order, tx);

    // Synchronize to read model
    const readModel = {
      orderId: order.id,
      customerId: order.customerId,
      status: order.status,
      total: order.total,
      placedAt: order.placedAt
    };
    await this.orderReadRepo.save(readModel, tx);
  });
}
```

### 5. Handle Eventual Consistency

If read and write models are updated asynchronously:

- Document **expected consistency lag**
- Design UIs to handle stale data gracefully
- Implement **cache invalidation** strategies
- Use **version numbers** or timestamps for cache validation

## Examples

### Command Implementation

```typescript
// commands/place-order.command.ts
interface PlaceOrderCommand {
  orderId?: string;        // Optional, generated if not provided
  customerId: string;
  items: OrderItem[];
  paymentMethod: 'CARD' | 'PAYPAL';
}

class PlaceOrderCommandHandler implements CommandHandler<PlaceOrderCommand> {
  async execute(command: PlaceOrderCommand): Promise<CommandResult> {
    // 1. Validate command
    const validation = this.validate(command);
    if (!validation.success) {
      return CommandResult.failure(validation.errors);
    }

    // 2. Check business invariants
    const customer = await this.customerRepo.findById(command.customerId);
    if (!customer.isActive) {
      return CommandResult.failure('Customer account is not active');
    }

    // 3. Create aggregate
    const order = Order.create({
      id: command.orderId,
      customerId: command.customerId,
      items: command.items
    });

    // 4. Persist
    await this.orderRepo.save(order);

    // 5. Emit event for async processing
    await this.eventBus.publish(OrderPlacedEvent.fromOrder(order));

    return CommandResult.success({ orderId: order.id });
  }
}
```

### Query Implementation

```typescript
// queries/get-order-details.query.ts
interface GetOrderDetailsQuery {
  orderId: string;
  includeItems?: boolean;
}

interface OrderDetailsReadModel {
  orderId: string;
  customerId: string;
  customerName: string;
  status: string;
  total: number;
  placedAt: string;
  items?: OrderItemReadModel[];
}

class GetOrderDetailsQueryHandler implements QueryHandler<GetOrderDetailsQuery, OrderDetailsReadModel> {
  async execute(query: GetOrderDetailsQuery): Promise<OrderDetailsReadModel> {
    const order = await this.readModelRepo.findOrderWithDetails(query.orderId);

    if (!order) {
      throw new QueryNotFoundError('Order not found');
    }

    const result: OrderDetailsReadModel = {
      orderId: order.orderId,
      customerId: order.customerId,
      customerName: order.customerName,
      status: order.status,
      total: order.total,
      placedAt: order.placedAt
    };

    if (query.includeItems) {
      result.items = await this.readModelRepo.findOrderItems(query.orderId);
    }

    return result;
  }
}
```

### Mediator Pattern for CQRS

```typescript
class CqrsMediator {
  private commandHandlers: Map<string, CommandHandler<any>>;
  private queryHandlers: Map<string, QueryHandler<any>>;

  async send<T>(message: Command | Query): Promise<CommandResult | QueryResult> {
    const handler = message instanceof Command
      ? this.commandHandlers.get(message.type)
      : this.queryHandlers.get(message.type);

    if (!handler) {
      throw new HandlerNotFoundError(message.type);
    }

    return handler.execute(message);
  }
}

// Usage
const result = await mediator.send(new PlaceOrderCommand({ ... }));
const orderDetails = await mediator.send(new GetOrderDetailsQuery({ orderId: '123' }));
```

## Related Skills

- api-design
- event-driven-architecture
- backend-patterns
- event-sourcing
- hexagonal-architecture

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.