[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6ESZCFTSR7G3FRRA9W0Q9FE":3,"skill-same:01M6ESZCFTSR7G3FRRA9W0Q9FE":69},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":34,"linkOnly":35,"markdownContent":36,"markdownBody":37,"frontMatter":38,"refName":40,"commitSha":41,"skillDirectory":42,"forksCount":43,"repositoryHtmlUrl":44,"repositoryHomepageUrl":45,"ownerHtmlUrl":46,"ownerBlogUrl":6,"ownerDescription":47,"updatedAt":48,"install":49,"resources":62},"01M6ESZCFTSR7G3FRRA9W0Q9FE","cqs-patterns",null,"Command Query Separation (CQS) and CQRS patterns for .NET. Use when designing methods, handlers, and application architecture. Ensures predictable, testable code.","ComeOnOliver\u002Fskillshub","ComeOnOliver","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F43215937?v=4","01M6E5JNFECJAAVFE0W7SMWWD2","Haoran Shu","01M6E5JNNDQS7AJZDYFQ8TPZCK",67,"MIT","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Faiskillstore\u002Fmarketplace\u002Fdoubleslashse\u002Fcqs-patterns\u002FSKILL.md",[],[19,20,21,22,23,24,25,26,27,28,29,30,31,32,33],"patterns","cqs patterns","architecture","code","grep","command","read","write","edit","use","methods","query","application","cqrs","separation","2026-11-10 10:12:30",false,"---\nname: cqs-patterns\ndescription: Command Query Separation (CQS) and CQRS patterns for .NET. Use when designing methods, handlers, and application architecture. Ensures predictable, testable code.\nallowed-tools: Read, Grep, Glob, Edit, Write\n---\n\n# Command Query Separation (CQS)\n\n## The Principle\n\n> A method should either be a **Command** that performs an action, or a **Query** that returns data, but not both.\n\n```\n┌─────────────────────────────────────────────────────────┐\n│                        METHOD                           │\n├───────────────────────┬─────────────────────────────────┤\n│       COMMAND         │            QUERY                │\n├───────────────────────┼─────────────────────────────────┤\n│ - Changes state       │ - Returns data                  │\n│ - Returns void        │ - No side effects               │\n│ - \"Do something\"      │ - \"Tell me something\"           │\n│ - Imperative verbs    │ - Noun or question              │\n│   (Create, Update,    │   (Get, Find, Is, Has,          │\n│    Delete, Process)   │    Calculate, Count)            │\n└───────────────────────┴─────────────────────────────────┘\n```\n\n---\n\n## CQS at Method Level\n\n### Violation Examples\n```csharp\n\u002F\u002F BAD: Method both modifies state AND returns data\npublic class Stack\u003CT>\n{\n    public T Pop()  \u002F\u002F Both removes item AND returns it\n    {\n        var item = _items[_count - 1];\n        _count--;\n        return item;\n    }\n}\n\n\u002F\u002F BAD: Getter with side effects\npublic class Counter\n{\n    private int _value;\n\n    public int Value\n    {\n        get { return _value++; }  \u002F\u002F Query that modifies state!\n    }\n}\n\n\u002F\u002F BAD: Command returns data\npublic class UserService\n{\n    public User CreateUser(string email, string name)  \u002F\u002F Returns created user\n    {\n        var user = new User { Email = email, Name = name };\n        _repository.Add(user);\n        return user;  \u002F\u002F CQS violation\n    }\n}\n```\n\n### Correct CQS Implementation\n```csharp\n\u002F\u002F GOOD: Separated commands and queries\npublic class Stack\u003CT>\n{\n    \u002F\u002F Query - returns data, no side effects\n    public T Peek()\n    {\n        if (_count == 0)\n            throw new InvalidOperationException(\"Stack is empty\");\n        return _items[_count - 1];\n    }\n\n    \u002F\u002F Command - modifies state, returns void\n    public void Pop()\n    {\n        if (_count == 0)\n            throw new InvalidOperationException(\"Stack is empty\");\n        _count--;\n    }\n\n    \u002F\u002F Usage: separate calls\n    var top = stack.Peek();\n    stack.Pop();\n}\n\n\u002F\u002F GOOD: Counter with pure query\npublic class Counter\n{\n    private int _value;\n\n    public int Value => _value;  \u002F\u002F Pure query\n\n    public void Increment() => _value++;  \u002F\u002F Command\n}\n\n\u002F\u002F GOOD: User service with CQS\npublic class UserService\n{\n    \u002F\u002F Command - creates user, returns identifier only\n    public Guid CreateUser(string email, string name)\n    {\n        var userId = Guid.NewGuid();\n        var user = new User { Id = userId, Email = email, Name = name };\n        _repository.Add(user);\n        return userId;  \u002F\u002F Returning ID is acceptable\n    }\n\n    \u002F\u002F Query - retrieves user\n    public User? GetUser(Guid userId)\n    {\n        return _repository.GetById(userId);\n    }\n}\n```\n\n---\n\n## CQS Exceptions\n\nSome situations justify combining command and query:\n\n### 1. Atomic Operations\n```csharp\n\u002F\u002F Acceptable: Interlocked operations need to be atomic\npublic int IncrementAndGet()\n{\n    return Interlocked.Increment(ref _counter);\n}\n\n\u002F\u002F Acceptable: Compare-and-swap patterns\npublic bool TryUpdate(int expected, int newValue)\n{\n    return Interlocked.CompareExchange(ref _value, newValue, expected) == expected;\n}\n```\n\n### 2. Fluent APIs\n```csharp\n\u002F\u002F Acceptable: Builder pattern returns this\npublic class QueryBuilder\n{\n    public QueryBuilder Where(string condition)\n    {\n        _conditions.Add(condition);\n        return this;  \u002F\u002F Returns modified builder\n    }\n\n    public QueryBuilder OrderBy(string column)\n    {\n        _orderBy = column;\n        return this;\n    }\n}\n```\n\n### 3. Factory Methods\n```csharp\n\u002F\u002F Acceptable: Creation returns the created object\npublic static Order Create(int customerId, IEnumerable\u003COrderItem> items)\n{\n    return new Order\n    {\n        Id = Guid.NewGuid(),\n        CustomerId = customerId,\n        Items = items.ToList()\n    };\n}\n```\n\n---\n\n## CQRS - Command Query Responsibility Segregation\n\nCQRS applies CQS at the **architectural level**, using separate models for reads and writes.\n\n```\n                    ┌─────────────────────────────────────┐\n                    │           APPLICATION               │\n                    └─────────────────────────────────────┘\n                                    │\n                    ┌───────────────┴───────────────┐\n                    ▼                               ▼\n           ┌───────────────┐               ┌───────────────┐\n           │   COMMANDS    │               │    QUERIES    │\n           │    (Write)    │               │    (Read)     │\n           └───────────────┘               └───────────────┘\n                    │                               │\n                    ▼                               ▼\n           ┌───────────────┐               ┌───────────────┐\n           │ Command       │               │ Query         │\n           │ Handlers      │               │ Handlers      │\n           └───────────────┘               └───────────────┘\n                    │                               │\n                    ▼                               ▼\n           ┌───────────────┐               ┌───────────────┐\n           │ Domain Model  │               │ Read Model    │\n           │ (Rich)        │               │ (DTOs)        │\n           └───────────────┘               └───────────────┘\n                    │                               │\n                    ▼                               ▼\n           ┌───────────────┐               ┌───────────────┐\n           │ Write DB      │──────────────▶│ Read DB       │\n           │ (Normalized)  │  Sync\u002FEvents  │ (Optimized)   │\n           └───────────────┘               └───────────────┘\n```\n\n### Commands and Queries\n\n```csharp\n\u002F\u002F === MARKER INTERFACES ===\n\npublic interface ICommand { }\n\npublic interface ICommand\u003CTResult> { }\n\npublic interface IQuery\u003CTResult> { }\n\n\u002F\u002F === COMMANDS ===\n\npublic record CreateOrderCommand(\n    int CustomerId,\n    List\u003COrderItemDto> Items\n) : ICommand\u003CGuid>;\n\npublic record UpdateOrderStatusCommand(\n    Guid OrderId,\n    OrderStatus NewStatus\n) : ICommand;\n\npublic record CancelOrderCommand(Guid OrderId) : ICommand;\n\n\u002F\u002F === QUERIES ===\n\npublic record GetOrderByIdQuery(Guid OrderId) : IQuery\u003COrderDto?>;\n\npublic record GetOrdersByCustomerQuery(\n    int CustomerId,\n    int Page = 1,\n    int PageSize = 10\n) : IQuery\u003CPagedResult\u003COrderSummaryDto>>;\n\npublic record GetOrderStatisticsQuery(\n    DateTime From,\n    DateTime To\n) : IQuery\u003COrderStatisticsDto>;\n```\n\n### Command Handlers\n\n```csharp\n\u002F\u002F === HANDLER INTERFACES ===\n\npublic interface ICommandHandler\u003Cin TCommand> where TCommand : ICommand\n{\n    Task HandleAsync(TCommand command, CancellationToken ct = default);\n}\n\npublic interface ICommandHandler\u003Cin TCommand, TResult> where TCommand : ICommand\u003CTResult>\n{\n    Task\u003CTResult> HandleAsync(TCommand command, CancellationToken ct = default);\n}\n\n\u002F\u002F === IMPLEMENTATION ===\n\npublic class CreateOrderCommandHandler : ICommandHandler\u003CCreateOrderCommand, Guid>\n{\n    private readonly IOrderRepository _orderRepository;\n    private readonly ICustomerRepository _customerRepository;\n    private readonly IEventPublisher _eventPublisher;\n\n    public CreateOrderCommandHandler(\n        IOrderRepository orderRepository,\n        ICustomerRepository customerRepository,\n        IEventPublisher eventPublisher)\n    {\n        _orderRepository = orderRepository;\n        _customerRepository = customerRepository;\n        _eventPublisher = eventPublisher;\n    }\n\n    public async Task\u003CGuid> HandleAsync(CreateOrderCommand command, CancellationToken ct)\n    {\n        \u002F\u002F Validate\n        var customer = await _customerRepository.GetByIdAsync(command.CustomerId, ct);\n        if (customer == null)\n            throw new CustomerNotFoundException(command.CustomerId);\n\n        \u002F\u002F Create domain entity\n        var order = Order.Create(command.CustomerId);\n        foreach (var item in command.Items)\n        {\n            order.AddItem(item.ProductId, item.Quantity, item.UnitPrice);\n        }\n\n        \u002F\u002F Persist\n        await _orderRepository.AddAsync(order, ct);\n\n        \u002F\u002F Publish domain event\n        await _eventPublisher.PublishAsync(new OrderCreatedEvent(order.Id), ct);\n\n        return order.Id;\n    }\n}\n\npublic class CancelOrderCommandHandler : ICommandHandler\u003CCancelOrderCommand>\n{\n    private readonly IOrderRepository _orderRepository;\n    private readonly IEventPublisher _eventPublisher;\n\n    public async Task HandleAsync(CancelOrderCommand command, CancellationToken ct)\n    {\n        var order = await _orderRepository.GetByIdAsync(command.OrderId, ct);\n        if (order == null)\n            throw new OrderNotFoundException(command.OrderId);\n\n        order.Cancel();\n\n        await _orderRepository.UpdateAsync(order, ct);\n        await _eventPublisher.PublishAsync(new OrderCancelledEvent(order.Id), ct);\n    }\n}\n```\n\n### Query Handlers\n\n```csharp\npublic interface IQueryHandler\u003Cin TQuery, TResult> where TQuery : IQuery\u003CTResult>\n{\n    Task\u003CTResult> HandleAsync(TQuery query, CancellationToken ct = default);\n}\n\npublic class GetOrderByIdQueryHandler : IQueryHandler\u003CGetOrderByIdQuery, OrderDto?>\n{\n    private readonly IDbConnection _connection;\n\n    public GetOrderByIdQueryHandler(IDbConnection connection)\n    {\n        _connection = connection;\n    }\n\n    public async Task\u003COrderDto?> HandleAsync(GetOrderByIdQuery query, CancellationToken ct)\n    {\n        \u002F\u002F Direct SQL for optimized reads\n        const string sql = @\"\n            SELECT o.Id, o.CustomerId, o.Status, o.Total, o.CreatedAt,\n                   c.Name as CustomerName, c.Email as CustomerEmail\n            FROM Orders o\n            JOIN Customers c ON o.CustomerId = c.Id\n            WHERE o.Id = @OrderId\";\n\n        return await _connection.QuerySingleOrDefaultAsync\u003COrderDto>(\n            new CommandDefinition(sql, new { query.OrderId }, cancellationToken: ct));\n    }\n}\n\npublic class GetOrdersByCustomerQueryHandler\n    : IQueryHandler\u003CGetOrdersByCustomerQuery, PagedResult\u003COrderSummaryDto>>\n{\n    private readonly IDbConnection _connection;\n\n    public async Task\u003CPagedResult\u003COrderSummaryDto>> HandleAsync(\n        GetOrdersByCustomerQuery query,\n        CancellationToken ct)\n    {\n        const string sql = @\"\n            SELECT Id, Status, Total, CreatedAt\n            FROM Orders\n            WHERE CustomerId = @CustomerId\n            ORDER BY CreatedAt DESC\n            OFFSET @Offset ROWS FETCH NEXT @PageSize ROWS ONLY;\n\n            SELECT COUNT(*) FROM Orders WHERE CustomerId = @CustomerId;\";\n\n        using var multi = await _connection.QueryMultipleAsync(\n            new CommandDefinition(sql,\n                new\n                {\n                    query.CustomerId,\n                    Offset = (query.Page - 1) * query.PageSize,\n                    query.PageSize\n                },\n                cancellationToken: ct));\n\n        var items = (await multi.ReadAsync\u003COrderSummaryDto>()).ToList();\n        var totalCount = await multi.ReadSingleAsync\u003Cint>();\n\n        return new PagedResult\u003COrderSummaryDto>(items, query.Page, query.PageSize, totalCount);\n    }\n}\n```\n\n### Dispatcher Pattern\n\n```csharp\npublic interface IDispatcher\n{\n    Task\u003CTResult> SendAsync\u003CTResult>(ICommand\u003CTResult> command, CancellationToken ct = default);\n    Task SendAsync(ICommand command, CancellationToken ct = default);\n    Task\u003CTResult> QueryAsync\u003CTResult>(IQuery\u003CTResult> query, CancellationToken ct = default);\n}\n\npublic class Dispatcher : IDispatcher\n{\n    private readonly IServiceProvider _serviceProvider;\n\n    public Dispatcher(IServiceProvider serviceProvider)\n    {\n        _serviceProvider = serviceProvider;\n    }\n\n    public async Task\u003CTResult> SendAsync\u003CTResult>(ICommand\u003CTResult> command, CancellationToken ct)\n    {\n        var handlerType = typeof(ICommandHandler\u003C,>).MakeGenericType(command.GetType(), typeof(TResult));\n        dynamic handler = _serviceProvider.GetRequiredService(handlerType);\n        return await handler.HandleAsync((dynamic)command, ct);\n    }\n\n    public async Task SendAsync(ICommand command, CancellationToken ct)\n    {\n        var handlerType = typeof(ICommandHandler\u003C>).MakeGenericType(command.GetType());\n        dynamic handler = _serviceProvider.GetRequiredService(handlerType);\n        await handler.HandleAsync((dynamic)command, ct);\n    }\n\n    public async Task\u003CTResult> QueryAsync\u003CTResult>(IQuery\u003CTResult> query, CancellationToken ct)\n    {\n        var handlerType = typeof(IQueryHandler\u003C,>).MakeGenericType(query.GetType(), typeof(TResult));\n        dynamic handler = _serviceProvider.GetRequiredService(handlerType);\n        return await handler.HandleAsync((dynamic)query, ct);\n    }\n}\n```\n\n### Usage in Controllers\n\n```csharp\n[ApiController]\n[Route(\"api\u002F[controller]\")]\npublic class OrdersController : ControllerBase\n{\n    private readonly IDispatcher _dispatcher;\n\n    public OrdersController(IDispatcher dispatcher)\n    {\n        _dispatcher = dispatcher;\n    }\n\n    [HttpPost]\n    public async Task\u003CActionResult\u003CGuid>> CreateOrder(\n        CreateOrderRequest request,\n        CancellationToken ct)\n    {\n        var command = new CreateOrderCommand(request.CustomerId, request.Items);\n        var orderId = await _dispatcher.SendAsync(command, ct);\n        return CreatedAtAction(nameof(GetOrder), new { id = orderId }, orderId);\n    }\n\n    [HttpGet(\"{id}\")]\n    public async Task\u003CActionResult\u003COrderDto>> GetOrder(Guid id, CancellationToken ct)\n    {\n        var query = new GetOrderByIdQuery(id);\n        var order = await _dispatcher.QueryAsync(query, ct);\n        return order == null ? NotFound() : Ok(order);\n    }\n\n    [HttpPost(\"{id}\u002Fcancel\")]\n    public async Task\u003CIActionResult> CancelOrder(Guid id, CancellationToken ct)\n    {\n        var command = new CancelOrderCommand(id);\n        await _dispatcher.SendAsync(command, ct);\n        return NoContent();\n    }\n}\n```\n\n---\n\n## Benefits of CQS\u002FCQRS\n\n| Benefit | Description |\n|---------|-------------|\n| **Testability** | Commands and queries are isolated, easy to test |\n| **Scalability** | Read and write sides can scale independently |\n| **Optimization** | Read models optimized for queries, write models for business rules |\n| **Clarity** | Clear intent - methods either change state or return data |\n| **Maintainability** | Single responsibility, easier to modify |\n| **Debugging** | Queries are safe to call repeatedly |\n\n---\n\n## When to Use CQRS\n\n### Good Fit\n- Complex domains with different read\u002Fwrite patterns\n- High-read, low-write scenarios\n- Systems requiring audit trails\n- Event-sourced systems\n- Microservices with separate read replicas\n\n### Not Needed\n- Simple CRUD applications\n- Small teams\u002Fprojects\n- Consistent read\u002Fwrite patterns\n- When added complexity outweighs benefits\n\n---\n\n## Quick Reference\n\n```csharp\n\u002F\u002F Pure Query - Safe, no side effects\npublic Customer? GetCustomer(int id);\npublic IReadOnlyList\u003COrder> FindOrdersByStatus(OrderStatus status);\npublic bool IsEmailAvailable(string email);\npublic int CountActiveUsers();\npublic decimal CalculateTotalRevenue(DateTime from, DateTime to);\n\n\u002F\u002F Pure Command - Changes state, returns void (or ID)\npublic void CreateCustomer(Customer customer);\npublic void UpdateOrderStatus(Guid orderId, OrderStatus status);\npublic void DeleteProduct(int productId);\npublic Guid PlaceOrder(OrderRequest request);  \u002F\u002F ID return is acceptable\n```\n","# Command Query Separation (CQS)\n\n## The Principle\n\n> A method should either be a **Command** that performs an action, or a **Query** that returns data, but not both.\n\n```\n┌─────────────────────────────────────────────────────────┐\n│                        METHOD                           │\n├───────────────────────┬─────────────────────────────────┤\n│       COMMAND         │            QUERY                │\n├───────────────────────┼─────────────────────────────────┤\n│ - Changes state       │ - Returns data                  │\n│ - Returns void        │ - No side effects               │\n│ - \"Do something\"      │ - \"Tell me something\"           │\n│ - Imperative verbs    │ - Noun or question              │\n│   (Create, Update,    │   (Get, Find, Is, Has,          │\n│    Delete, Process)   │    Calculate, Count)            │\n└───────────────────────┴─────────────────────────────────┘\n```\n\n---\n\n## CQS at Method Level\n\n### Violation Examples\n```csharp\n\u002F\u002F BAD: Method both modifies state AND returns data\npublic class Stack\u003CT>\n{\n    public T Pop()  \u002F\u002F Both removes item AND returns it\n    {\n        var item = _items[_count - 1];\n        _count--;\n        return item;\n    }\n}\n\n\u002F\u002F BAD: Getter with side effects\npublic class Counter\n{\n    private int _value;\n\n    public int Value\n    {\n        get { return _value++; }  \u002F\u002F Query that modifies state!\n    }\n}\n\n\u002F\u002F BAD: Command returns data\npublic class UserService\n{\n    public User CreateUser(string email, string name)  \u002F\u002F Returns created user\n    {\n        var user = new User { Email = email, Name = name };\n        _repository.Add(user);\n        return user;  \u002F\u002F CQS violation\n    }\n}\n```\n\n### Correct CQS Implementation\n```csharp\n\u002F\u002F GOOD: Separated commands and queries\npublic class Stack\u003CT>\n{\n    \u002F\u002F Query - returns data, no side effects\n    public T Peek()\n    {\n        if (_count == 0)\n            throw new InvalidOperationException(\"Stack is empty\");\n        return _items[_count - 1];\n    }\n\n    \u002F\u002F Command - modifies state, returns void\n    public void Pop()\n    {\n        if (_count == 0)\n            throw new InvalidOperationException(\"Stack is empty\");\n        _count--;\n    }\n\n    \u002F\u002F Usage: separate calls\n    var top = stack.Peek();\n    stack.Pop();\n}\n\n\u002F\u002F GOOD: Counter with pure query\npublic class Counter\n{\n    private int _value;\n\n    public int Value => _value;  \u002F\u002F Pure query\n\n    public void Increment() => _value++;  \u002F\u002F Command\n}\n\n\u002F\u002F GOOD: User service with CQS\npublic class UserService\n{\n    \u002F\u002F Command - creates user, returns identifier only\n    public Guid CreateUser(string email, string name)\n    {\n        var userId = Guid.NewGuid();\n        var user = new User { Id = userId, Email = email, Name = name };\n        _repository.Add(user);\n        return userId;  \u002F\u002F Returning ID is acceptable\n    }\n\n    \u002F\u002F Query - retrieves user\n    public User? GetUser(Guid userId)\n    {\n        return _repository.GetById(userId);\n    }\n}\n```\n\n---\n\n## CQS Exceptions\n\nSome situations justify combining command and query:\n\n### 1. Atomic Operations\n```csharp\n\u002F\u002F Acceptable: Interlocked operations need to be atomic\npublic int IncrementAndGet()\n{\n    return Interlocked.Increment(ref _counter);\n}\n\n\u002F\u002F Acceptable: Compare-and-swap patterns\npublic bool TryUpdate(int expected, int newValue)\n{\n    return Interlocked.CompareExchange(ref _value, newValue, expected) == expected;\n}\n```\n\n### 2. Fluent APIs\n```csharp\n\u002F\u002F Acceptable: Builder pattern returns this\npublic class QueryBuilder\n{\n    public QueryBuilder Where(string condition)\n    {\n        _conditions.Add(condition);\n        return this;  \u002F\u002F Returns modified builder\n    }\n\n    public QueryBuilder OrderBy(string column)\n    {\n        _orderBy = column;\n        return this;\n    }\n}\n```\n\n### 3. Factory Methods\n```csharp\n\u002F\u002F Acceptable: Creation returns the created object\npublic static Order Create(int customerId, IEnumerable\u003COrderItem> items)\n{\n    return new Order\n    {\n        Id = Guid.NewGuid(),\n        CustomerId = customerId,\n        Items = items.ToList()\n    };\n}\n```\n\n---\n\n## CQRS - Command Query Responsibility Segregation\n\nCQRS applies CQS at the **architectural level**, using separate models for reads and writes.\n\n```\n                    ┌─────────────────────────────────────┐\n                    │           APPLICATION               │\n                    └─────────────────────────────────────┘\n                                    │\n                    ┌───────────────┴───────────────┐\n                    ▼                               ▼\n           ┌───────────────┐               ┌───────────────┐\n           │   COMMANDS    │               │    QUERIES    │\n           │    (Write)    │               │    (Read)     │\n           └───────────────┘               └───────────────┘\n                    │                               │\n                    ▼                               ▼\n           ┌───────────────┐               ┌───────────────┐\n           │ Command       │               │ Query         │\n           │ Handlers      │               │ Handlers      │\n           └───────────────┘               └───────────────┘\n                    │                               │\n                    ▼                               ▼\n           ┌───────────────┐               ┌───────────────┐\n           │ Domain Model  │               │ Read Model    │\n           │ (Rich)        │               │ (DTOs)        │\n           └───────────────┘               └───────────────┘\n                    │                               │\n                    ▼                               ▼\n           ┌───────────────┐               ┌───────────────┐\n           │ Write DB      │──────────────▶│ Read DB       │\n           │ (Normalized)  │  Sync\u002FEvents  │ (Optimized)   │\n           └───────────────┘               └───────────────┘\n```\n\n### Commands and Queries\n\n```csharp\n\u002F\u002F === MARKER INTERFACES ===\n\npublic interface ICommand { }\n\npublic interface ICommand\u003CTResult> { }\n\npublic interface IQuery\u003CTResult> { }\n\n\u002F\u002F === COMMANDS ===\n\npublic record CreateOrderCommand(\n    int CustomerId,\n    List\u003COrderItemDto> Items\n) : ICommand\u003CGuid>;\n\npublic record UpdateOrderStatusCommand(\n    Guid OrderId,\n    OrderStatus NewStatus\n) : ICommand;\n\npublic record CancelOrderCommand(Guid OrderId) : ICommand;\n\n\u002F\u002F === QUERIES ===\n\npublic record GetOrderByIdQuery(Guid OrderId) : IQuery\u003COrderDto?>;\n\npublic record GetOrdersByCustomerQuery(\n    int CustomerId,\n    int Page = 1,\n    int PageSize = 10\n) : IQuery\u003CPagedResult\u003COrderSummaryDto>>;\n\npublic record GetOrderStatisticsQuery(\n    DateTime From,\n    DateTime To\n) : IQuery\u003COrderStatisticsDto>;\n```\n\n### Command Handlers\n\n```csharp\n\u002F\u002F === HANDLER INTERFACES ===\n\npublic interface ICommandHandler\u003Cin TCommand> where TCommand : ICommand\n{\n    Task HandleAsync(TCommand command, CancellationToken ct = default);\n}\n\npublic interface ICommandHandler\u003Cin TCommand, TResult> where TCommand : ICommand\u003CTResult>\n{\n    Task\u003CTResult> HandleAsync(TCommand command, CancellationToken ct = default);\n}\n\n\u002F\u002F === IMPLEMENTATION ===\n\npublic class CreateOrderCommandHandler : ICommandHandler\u003CCreateOrderCommand, Guid>\n{\n    private readonly IOrderRepository _orderRepository;\n    private readonly ICustomerRepository _customerRepository;\n    private readonly IEventPublisher _eventPublisher;\n\n    public CreateOrderCommandHandler(\n        IOrderRepository orderRepository,\n        ICustomerRepository customerRepository,\n        IEventPublisher eventPublisher)\n    {\n        _orderRepository = orderRepository;\n        _customerRepository = customerRepository;\n        _eventPublisher = eventPublisher;\n    }\n\n    public async Task\u003CGuid> HandleAsync(CreateOrderCommand command, CancellationToken ct)\n    {\n        \u002F\u002F Validate\n        var customer = await _customerRepository.GetByIdAsync(command.CustomerId, ct);\n        if (customer == null)\n            throw new CustomerNotFoundException(command.CustomerId);\n\n        \u002F\u002F Create domain entity\n        var order = Order.Create(command.CustomerId);\n        foreach (var item in command.Items)\n        {\n            order.AddItem(item.ProductId, item.Quantity, item.UnitPrice);\n        }\n\n        \u002F\u002F Persist\n        await _orderRepository.AddAsync(order, ct);\n\n        \u002F\u002F Publish domain event\n        await _eventPublisher.PublishAsync(new OrderCreatedEvent(order.Id), ct);\n\n        return order.Id;\n    }\n}\n\npublic class CancelOrderCommandHandler : ICommandHandler\u003CCancelOrderCommand>\n{\n    private readonly IOrderRepository _orderRepository;\n    private readonly IEventPublisher _eventPublisher;\n\n    public async Task HandleAsync(CancelOrderCommand command, CancellationToken ct)\n    {\n        var order = await _orderRepository.GetByIdAsync(command.OrderId, ct);\n        if (order == null)\n            throw new OrderNotFoundException(command.OrderId);\n\n        order.Cancel();\n\n        await _orderRepository.UpdateAsync(order, ct);\n        await _eventPublisher.PublishAsync(new OrderCancelledEvent(order.Id), ct);\n    }\n}\n```\n\n### Query Handlers\n\n```csharp\npublic interface IQueryHandler\u003Cin TQuery, TResult> where TQuery : IQuery\u003CTResult>\n{\n    Task\u003CTResult> HandleAsync(TQuery query, CancellationToken ct = default);\n}\n\npublic class GetOrderByIdQueryHandler : IQueryHandler\u003CGetOrderByIdQuery, OrderDto?>\n{\n    private readonly IDbConnection _connection;\n\n    public GetOrderByIdQueryHandler(IDbConnection connection)\n    {\n        _connection = connection;\n    }\n\n    public async Task\u003COrderDto?> HandleAsync(GetOrderByIdQuery query, CancellationToken ct)\n    {\n        \u002F\u002F Direct SQL for optimized reads\n        const string sql = @\"\n            SELECT o.Id, o.CustomerId, o.Status, o.Total, o.CreatedAt,\n                   c.Name as CustomerName, c.Email as CustomerEmail\n            FROM Orders o\n            JOIN Customers c ON o.CustomerId = c.Id\n            WHERE o.Id = @OrderId\";\n\n        return await _connection.QuerySingleOrDefaultAsync\u003COrderDto>(\n            new CommandDefinition(sql, new { query.OrderId }, cancellationToken: ct));\n    }\n}\n\npublic class GetOrdersByCustomerQueryHandler\n    : IQueryHandler\u003CGetOrdersByCustomerQuery, PagedResult\u003COrderSummaryDto>>\n{\n    private readonly IDbConnection _connection;\n\n    public async Task\u003CPagedResult\u003COrderSummaryDto>> HandleAsync(\n        GetOrdersByCustomerQuery query,\n        CancellationToken ct)\n    {\n        const string sql = @\"\n            SELECT Id, Status, Total, CreatedAt\n            FROM Orders\n            WHERE CustomerId = @CustomerId\n            ORDER BY CreatedAt DESC\n            OFFSET @Offset ROWS FETCH NEXT @PageSize ROWS ONLY;\n\n            SELECT COUNT(*) FROM Orders WHERE CustomerId = @CustomerId;\";\n\n        using var multi = await _connection.QueryMultipleAsync(\n            new CommandDefinition(sql,\n                new\n                {\n                    query.CustomerId,\n                    Offset = (query.Page - 1) * query.PageSize,\n                    query.PageSize\n                },\n                cancellationToken: ct));\n\n        var items = (await multi.ReadAsync\u003COrderSummaryDto>()).ToList();\n        var totalCount = await multi.ReadSingleAsync\u003Cint>();\n\n        return new PagedResult\u003COrderSummaryDto>(items, query.Page, query.PageSize, totalCount);\n    }\n}\n```\n\n### Dispatcher Pattern\n\n```csharp\npublic interface IDispatcher\n{\n    Task\u003CTResult> SendAsync\u003CTResult>(ICommand\u003CTResult> command, CancellationToken ct = default);\n    Task SendAsync(ICommand command, CancellationToken ct = default);\n    Task\u003CTResult> QueryAsync\u003CTResult>(IQuery\u003CTResult> query, CancellationToken ct = default);\n}\n\npublic class Dispatcher : IDispatcher\n{\n    private readonly IServiceProvider _serviceProvider;\n\n    public Dispatcher(IServiceProvider serviceProvider)\n    {\n        _serviceProvider = serviceProvider;\n    }\n\n    public async Task\u003CTResult> SendAsync\u003CTResult>(ICommand\u003CTResult> command, CancellationToken ct)\n    {\n        var handlerType = typeof(ICommandHandler\u003C,>).MakeGenericType(command.GetType(), typeof(TResult));\n        dynamic handler = _serviceProvider.GetRequiredService(handlerType);\n        return await handler.HandleAsync((dynamic)command, ct);\n    }\n\n    public async Task SendAsync(ICommand command, CancellationToken ct)\n    {\n        var handlerType = typeof(ICommandHandler\u003C>).MakeGenericType(command.GetType());\n        dynamic handler = _serviceProvider.GetRequiredService(handlerType);\n        await handler.HandleAsync((dynamic)command, ct);\n    }\n\n    public async Task\u003CTResult> QueryAsync\u003CTResult>(IQuery\u003CTResult> query, CancellationToken ct)\n    {\n        var handlerType = typeof(IQueryHandler\u003C,>).MakeGenericType(query.GetType(), typeof(TResult));\n        dynamic handler = _serviceProvider.GetRequiredService(handlerType);\n        return await handler.HandleAsync((dynamic)query, ct);\n    }\n}\n```\n\n### Usage in Controllers\n\n```csharp\n[ApiController]\n[Route(\"api\u002F[controller]\")]\npublic class OrdersController : ControllerBase\n{\n    private readonly IDispatcher _dispatcher;\n\n    public OrdersController(IDispatcher dispatcher)\n    {\n        _dispatcher = dispatcher;\n    }\n\n    [HttpPost]\n    public async Task\u003CActionResult\u003CGuid>> CreateOrder(\n        CreateOrderRequest request,\n        CancellationToken ct)\n    {\n        var command = new CreateOrderCommand(request.CustomerId, request.Items);\n        var orderId = await _dispatcher.SendAsync(command, ct);\n        return CreatedAtAction(nameof(GetOrder), new { id = orderId }, orderId);\n    }\n\n    [HttpGet(\"{id}\")]\n    public async Task\u003CActionResult\u003COrderDto>> GetOrder(Guid id, CancellationToken ct)\n    {\n        var query = new GetOrderByIdQuery(id);\n        var order = await _dispatcher.QueryAsync(query, ct);\n        return order == null ? NotFound() : Ok(order);\n    }\n\n    [HttpPost(\"{id}\u002Fcancel\")]\n    public async Task\u003CIActionResult> CancelOrder(Guid id, CancellationToken ct)\n    {\n        var command = new CancelOrderCommand(id);\n        await _dispatcher.SendAsync(command, ct);\n        return NoContent();\n    }\n}\n```\n\n---\n\n## Benefits of CQS\u002FCQRS\n\n| Benefit | Description |\n|---------|-------------|\n| **Testability** | Commands and queries are isolated, easy to test |\n| **Scalability** | Read and write sides can scale independently |\n| **Optimization** | Read models optimized for queries, write models for business rules |\n| **Clarity** | Clear intent - methods either change state or return data |\n| **Maintainability** | Single responsibility, easier to modify |\n| **Debugging** | Queries are safe to call repeatedly |\n\n---\n\n## When to Use CQRS\n\n### Good Fit\n- Complex domains with different read\u002Fwrite patterns\n- High-read, low-write scenarios\n- Systems requiring audit trails\n- Event-sourced systems\n- Microservices with separate read replicas\n\n### Not Needed\n- Simple CRUD applications\n- Small teams\u002Fprojects\n- Consistent read\u002Fwrite patterns\n- When added complexity outweighs benefits\n\n---\n\n## Quick Reference\n\n```csharp\n\u002F\u002F Pure Query - Safe, no side effects\npublic Customer? GetCustomer(int id);\npublic IReadOnlyList\u003COrder> FindOrdersByStatus(OrderStatus status);\npublic bool IsEmailAvailable(string email);\npublic int CountActiveUsers();\npublic decimal CalculateTotalRevenue(DateTime from, DateTime to);\n\n\u002F\u002F Pure Command - Changes state, returns void (or ID)\npublic void CreateCustomer(Customer customer);\npublic void UpdateOrderStatus(Guid orderId, OrderStatus status);\npublic void DeleteProduct(int productId);\npublic Guid PlaceOrder(OrderRequest request);  \u002F\u002F ID return is acceptable\n```\n",{"name":5,"description":7,"allowed-tools":39},"Read, Grep, Glob, Edit, Write","main","def8531e65114c0fca8fb8551c1871ee0eed705c","skills\u002Faiskillstore\u002Fmarketplace\u002Fdoubleslashse\u002Fcqs-patterns",24,"https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub","https:\u002F\u002Fskillshub.wtf","https:\u002F\u002Fgithub.com\u002FComeOnOliver","AI & Blockchain Engineer\r\n","2026-06-24 22:07:19",{"source":8,"skillName":5,"commands":50,"prompt":60,"zipAvailable":61},[51,54,57],{"tool":52,"command":53},"npx","npx skills add ComeOnOliver\u002Fskillshub --skill cqs-patterns",{"tool":55,"command":56},"bunx","bunx skills add ComeOnOliver\u002Fskillshub --skill cqs-patterns",{"tool":58,"command":59},"pnpm","pnpm dlx skills add ComeOnOliver\u002Fskillshub --skill cqs-patterns","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Faiskillstore\u002Fmarketplace\u002Fdoubleslashse\u002Fcqs-patterns\u002FSKILL.md",true,[63],{"path":64,"resourceType":65,"resourceTypeDesc":66,"sizeBytes":67,"sourceHtmlUrl":68},"skill-report.json","REFERENCE","参考文档",10971,"https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Faiskillstore\u002Fmarketplace\u002Fdoubleslashse\u002Fcqs-patterns\u002Fskill-report.json",[70,84,94,103,109,116],{"skillNo":71,"name":72,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":73,"categories":74,"keywords":80,"publishedAt":34},"01M6FKJN13GMG4KY57J3QD8GY5","model-hierarchy-skill","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzscole\u002Fmodel-hierarchy-skill\u002Fmodel-hierarchy-skill\u002FSKILL.md",[75],{"categoryNo":76,"categoryName":77,"taxonomyType":78,"taxonomyTypeDesc":79},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[81,82,83],"model","model hierarchy","model hierarchy skill",{"skillNo":85,"name":86,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":87,"categories":88,"keywords":90,"publishedAt":34},"01M6FKJJJ6GCAMR4PPPRD12M5X","zoom-mcp","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-mcp\u002FSKILL.md",[89],{"categoryNo":76,"categoryName":77,"taxonomyType":78,"taxonomyTypeDesc":79},[91,92,93],"MCP","zoom","zoom mcp",{"skillNo":95,"name":96,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":97,"categories":98,"keywords":99,"publishedAt":34},"01M6FKJE3KYT3KPEZMEEHYKA6K","zoom-apps-sdk","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-apps-sdk\u002FSKILL.md",[],[100,101,102,92],"sdk","zoom apps sdk","apps",{"skillNo":104,"name":105,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":106,"categories":107,"keywords":108,"publishedAt":34},"01M6FKJ9EDH9Q5VWDF8HAJRSP0","windows","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwindows\u002FSKILL.md",[],[105],{"skillNo":110,"name":111,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":112,"categories":113,"keywords":115,"publishedAt":34},"01M6FKJ666BEEFC3AE9GJ72Q93","whiteboard","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwhiteboard\u002FSKILL.md",[114],{"categoryNo":76,"categoryName":77,"taxonomyType":78,"taxonomyTypeDesc":79},[111],{"skillNo":117,"name":118,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":119,"categories":120,"keywords":121,"publishedAt":34},"01M6FKJ21WFCDGTJQ2Q91T9DVW","websockets","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwebsockets\u002FSKILL.md",[],[118]]