← Back to list

CQRS Isn’t Always the Answer: A Simpler Approach That Worked

A short, practical deep dive with real-world examples in C# .NET

Abhishek Joshi in CodeToDeploy · 2026-05-25 15:07 · 102 claps · 2.3 min read paywalled
#cqrs #software-design-patterns #csharp #dot-net-core #system-design-concepts
Open on Medium ↗

CQRS Isn’t Always the Answer: A Simpler Approach That Worked

A short, practical deep dive with real-world examples in C# .NET

We didn’t remove CQRS. We stopped applying it everywhere.

Client service example

Client service example

CQRS (Command Query Responsibility Segregation) sounds elegant:

  • Commands → Change data
  • Queries → Read data

🚨 HIRING: Tech Talent 💰 $50–$120/hr | 🔥 Multiple Roles

Frontend • Backend • Full Stack • Mobile • AI/ML • DevOps 👉 **Apply Here**

Simple example:

A food delivery app:

  • Place Order → Command (changes system state)
  • View Restaurants → Query (just reads data)

That separation works well.

Until every tiny feature starts creating extra files.

Where Things Started Breaking

Our .NET application adopted CQRS for everything. Even simple features looked like this:

CreateCustomerCommand
CreateCustomerHandler
CustomerDto
Mapper
Validator
GetCustomerQuery
GetCustomerHandler

Adding one field meant touching many files.

A 30-minute change became a full-day task.

Architecture started slowing development.

The Turning Point

One question changed everything:

“Does this feature actually need CQRS?”

New rule:

Complexity earns CQRS.

Not every endpoint deserves full separation.

What We Changed

Keep CQRS for important business actions

Examples:

  • Approve Loan
  • Place Order
  • Transfer Money
  • Generate Invoice

C# example:

public record PlaceOrderCommand(
    int CustomerId,
    decimal Amount
) : IRequest<int>;

Handler contains:

  • validation
  • business rules
  • events
  • audit logging

Perfect use case.

Simplify simple reads

Instead of:

GetCustomersQuery
GetCustomersHandler

We used:

[HttpGet]
public async Task<List<CustomerDto>>
GetCustomers()
{
    return await db.Customers
        .Select(c =>
            new CustomerDto(
                c.Id,
                c.Name))
        .ToListAsync();
}

Short.

Readable.

Easy to maintain.

Real-Life Analogy

Airport system:

Query

“Which gate is my flight?”

Simple read.

Command

“Cancel the flight.”

Needs:

  • notifications
  • refunds
  • schedule updates

That deserves CQRS.

CQRS-flow

CQRS-flow

Final Architecture

Features
│
├── Orders
│   └── Commands
│
├── Customers
│   └── Direct Queries
│
└── Shared

Hybrid approach.

Not strict CQRS.

Practical CQRS.

Go-To Notes

Simple Read → Direct Query

Business Action → Command

Complex Workflow → CQRS

CRUD Screen → Keep Simple

Scale Needs → Separate Reads/Writes

Final takeaway

CQRS is a tool — not a rule. If architecture makes simple work difficult, simplify it. Good architecture protects complexity. It shouldn’t create it.

*Thanks for reading!* If you found this article helpful, feel free to give👏 claps, 🛎️subscribe, 💬comment, and 📢 share* *with fellow developers.

You can 🛎️ subscribe here for regular articles.

Thank you for being a part of the community

Before you go:

👉 Be sure to clap and follow the writer ️👏️️

👉 Follow us: **Linkedin| [Medium](https://medium.com/codetodeploy)**

👉 CodeToDeploy Tech Community is live on Discord — **Join now!**

Disclosure: This post includes affiliate and partnership links.


메타데이터
post_id
90acabbd013b
slug
cqrs-isnt-always-the-answer-a-simpler-approach-that-worked-90acabbd013b
url
https://medium.com/codetodeploy/cqrs-isnt-always-the-answer-a-simpler-approach-that-worked-90acabbd013b
canonical_url
https://medium.com/codetodeploy/cqrs-isnt-always-the-answer-a-simpler-approach-that-worked-90acabbd013b
author_url
https://medium.com/@joshiabhi777
status
ok
fetched_at
2026-06-09 15:37:30