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.

Work With Prilixor

Get in touch