Türkçe
Clean Architecture (CQRS)

What Actually Changes When You Split Reads From Writes With CQRS

One apartment management system saw query performance jump 40 percent.

· Full Stack Developer · Updated:

Once an API starts growing, the first bottleneck is usually that reads and writes run through the exact same code path. Clean Architecture addresses that by splitting the application into layers, keeping business rules independent of the database and the framework. CQRS goes a step further and separates read operations (queries) from write operations (commands) entirely. MediatR is the small .NET library that routes each command or query to the handler responsible for it.

I use CQRS together with MediatR on multi-tenant APIs. Every command and query lives in its own handler, and the controller's only job is to pass the request along to MediatR. That separation starts paying off once the data grows and the number of tenants goes up.

I saw the concrete effect of this on an apartment management system built by a two-person team: after splitting reads from writes with CQRS, query performance improved by 40 percent. I set up the same Clean Architecture plus CQRS structure on an AI-powered hotel reservation assistant and on a cloud-based document management system I built solo.

It looks like more layers on paper, but knowing exactly where to look once the codebase grows is what actually saves time.

MediatR's real value isn't just routing a command or query to the right handler, it's the pipeline behaviors you can hook into that routing. Cross-cutting concerns like validation, logging or transaction handling go into an IPipelineBehavior once, instead of being copy-pasted into every handler, and every new command or query passes through them automatically. That keeps each handler focused on its own business logic instead of repeating the same validation or logging boilerplate.

Mentioning CQRS makes people assume it needs a separate read database or event sourcing, but neither one is actually required. On the projects I've built, reads and writes hit the same database, they just go through separate models and separate queries on the code side. For a small, CRUD-heavy internal tool, setting up that separation from day one just means more files and more layers, and fully wiring up CQRS on something with a handful of screens and one developer can cost more in complexity than it saves.