Prilixor
All blogs

Design Patterns

CQRS and MediatR in .NET: Simplifying Complex Domain Logic

As .NET applications grow in size and complexity, one of the biggest challenges developers face is keeping domain logic understandable and maintainable. Features accumulate, business rules evolve, and suddenly a once-simple service layer becomes a tangled web of mixed responsibilities.

· 5 min read
Share

As .NET applications grow in size and complexity, one of the biggest challenges developers face is keeping domain logic understandable and maintainable. Features accumulate, business rules evolve, and suddenly a once-simple service layer becomes a tangled web of mixed responsibilities.

Two powerful concepts that help bring structure back into this complexity are CQRS (Command Query Responsibility Segregation) and MediatR. Together, they provide a clean way to organize behavior, separate reads from writes, and simplify the flow of domain logic in modern .NET applications.

What Is CQRS?

Command Query Responsibility Segregation (CQRS) is a pattern that separates operations that change state (commands) from operations that read state (queries).

  • Commands:
  • Queries:

Instead of having a single service that both reads and writes, CQRS encourages you to treat these as two different responsibilities. That simple separation has a big impact on clarity and scalability.

Why CQRS Helps in Real Applications

1. Clearer Separation of Concerns

By isolating reads and writes, each side becomes easier to reason about:

  • Write logic focuses on business rules, validation, domain events, and state changes.
  • Read logic focuses on efficient data retrieval and mapping to the shape the UI needs.

This separation reduces coupling and keeps classes smaller and more focused.

2. Easier to Scale and Optimize

Read and write workloads often have very different performance requirements:

  • Reads may need caching, denormalized views, or separate read models.
  • Writes may need transactional guarantees, business invariants, and auditing.

With CQRS, you can tune each side independently — scale read-heavy operations differently from write-heavy ones, or even use different data stores if needed.

3. Improved Maintainability

Because commands and queries are explicit and isolated:

  • It’s easier to see where a particular piece of behavior lives.
  • New developers can quickly understand how a feature flows.
  • Refactoring becomes less risky, because responsibilities are already split.

CQRS doesn’t have to be “all or nothing.” Even partial adoption in complex areas of a system can create noticeable structure and clarity.

Where MediatR Fits In

MediatR is a lightweight library for .NET that implements the mediator pattern. Instead of components calling each other directly, they communicate through a central mediator.

In a CQRS-style application, MediatR is often used as:

  • The entry point for commands and queries
  • A dispatcher that routes each request to the appropriate handler
  • A pipeline where cross-cutting concerns (logging, validation, authorization, etc.) can be applied

This creates a very clean structure:

  1. The UI or API layer sends a command/query to MediatR.
  2. MediatR finds the matching handler.
  3. The handler contains the core logic for that operation.

No controller or service needs to know how things are done — only what to send.

Benefits of Using MediatR with CQRS

1. Centralized Request Handling

Each command or query has a dedicated handler:

  • One place to look for each feature’s logic
  • No “god services” that handle dozens of operations
  • Highly discoverable codebase — feature = request + handler

2. Looser Coupling Between Layers

Controllers, UI components, or background jobs don’t depend directly on domain services. Instead, they depend on an abstraction (sending a request through MediatR). This reduces coupling and makes it easier to swap implementations or refactor internals without touching entry points.

3. Cross-Cutting Concerns Made Elegant

MediatR supports pipeline behaviors — a powerful way to plug in common concerns:

  • Validation
  • Logging
  • Caching
  • Authorization
  • Performance metrics

Instead of duplicating this logic in every handler or controller, you apply it once in the pipeline.

4. Improved Testability

Because command and query handlers are small, focused, and depend on abstractions:

  • They can be tested independently
  • Dependencies can be mocked easily
  • Each use case has clear inputs and outputs

This fits perfectly with clean architecture and domain-driven design approaches.

When to Use CQRS and MediatR

CQRS with MediatR is especially helpful when:

  • Your domain logic is becoming complex and hard to follow
  • The same service or controller method is doing too much
  • Read and write performance concerns are different
  • You want a more explicit, use-case–driven structure
  • You’re moving toward clean architecture or domain-driven design

For very small or simple applications, full CQRS might be overkill. But for medium and large systems, especially those evolving over time, it can dramatically improve structure and scalability.

A Typical Flow in a CQRS + MediatR .NET Application

  1. A user performs an action in the UI or sends a request via API.
  2. The controller (or endpoint) creates a Command or Query representing that action.
  3. The command/query is sent to MediatR.
  4. MediatR invokes the Handler responsible for that request.
  5. The handler coordinates domain logic, repositories, and external services.
  6. For commands, it applies changes; for queries, it returns data.
  7. The result is sent back to the UI or API response.

Every use case becomes explicit, traceable, and testable.

Final Thoughts

CQRS and MediatR together offer .NET developers a powerful way to simplify complex domain logic:

  • CQRS splits reads and writes, clarifying responsibilities.
  • MediatR provides a clean, organized way to route and handle those operations.

The result is a codebase with:

  • Better separation of concerns
  • Clearer structure and feature boundaries
  • Improved scalability and performance opportunities
  • Easier testing and safer refactoring

As applications and teams grow, patterns like CQRS and tools like MediatR can make the difference between a system that merely works and one that remains clean, adaptable, and sustainable over the long term.

Keep reading

Related posts

All blogs →

Azure & Cloud 3 min read

Avoiding Vendor Lock-In While Still Using Azure Effectively

In today's fast-paced digital landscape, organizations are continually adopting cloud solutions to drive innovation and improve efficiency. However, while platforms like Azure offer powerful tools and services, they also present the challenge of vendor lock-in. This can limit an organization’s flexibility to migrate applications or services as needs evolve. The key to leveraging Azure’s capabilities effectively lies in strategic planning and decision-making.

1 Sep 2026

Security 3 min read

Understanding Security Boundaries in Azure: Where Trust Actually Ends

In an era dominated by rapid digital transformation, understanding security boundaries in cloud environments has never been more crucial. Azure, one of the leading cloud service providers, offers a plethora of tools and services designed to enhance security. However, it's imperative to acknowledge that even the most sophisticated systems have limitations. Trust and security boundaries define where end-to-end protections end and where potential vulnerabilities may arise.

31 Aug 2026

Azure & Cloud 3 min read

Navigating Cold Starts, Scaling, and Throttling in Azure Services

In today's digital landscape, the demand for scalable and efficient cloud solutions has never been greater. Organizations rely on cloud services to remain agile, yet they often face challenges that can hinder performance and user experience. Among these are cold starts, scaling decisions, and throttling constraints, particularly within Azure services.

30 Aug 2026