We’ll explore this architecture through a mini ToDo List project in a two-part series.

Article 1:

This article explains what Vertical Slice Architecture and CQRS are, why they complement each other, when to use them (and when not to), includes a simple diagram and high-level request flow, explores real-world use cases, outlines pros and cons, and compares this approach to traditional layered architecture.

Article 2:

The implementation: how the ToDo API is built. You can read it here: Modern Backend Architecture in .NET – Implementing Vertical Slice + CQRS

You’ll see the actual project structure, how Commands and Queries are implemented using MediatR, how FluentValidation integrates into the pipeline, and a step-by-step walkthrough of how a request flows from endpoint → MediatR → handler → database → response.

The Project We're Building

We use one concrete example throughout both articles: a Todo API. The app lets users create, read, update, and delete todo items (each has a title, an optional description, and a completed flag). It’s a small, familiar domain so we can focus on architecture instead of business rules.

Because the whole project is built around this Todo feature, you’ll see the word Todo everywhere: Todo entity, CreateTodo command, GetTodo query, TodoEndpoints, Features/Todos/, and so on. Once you see how Vertical Slice and CQRS work with Todos, you can apply the same ideas to any other feature (e.g., Orders, Invoices, Users).

You can find project here: https://github.com/RikamPalkar/vertical-cqrs-architecture-dotnet/tree/main

1. What is Vertical Slice Architecture?

Vertical Slice Architecture means organizing code by feature (or by “use case”), not by layer.

In this project, the “Todo” feature is split into slices like:

In one sentence: You organize by “what the system does” (features/use cases), not by “what kind of class it is” (controllers, services, repositories).

Where entities and DTOs live

In this style of project:

2. What is CQRS?

CQRS stands for Command Query Responsibility Segregation.

So you separate:

In this project:

Each command/query has one handler that contains the logic. MediatR is the library that “sends” the command or query to the right handler.

Purpose of Command, Handler, and Validator

In each slice you typically have three kinds of pieces.

3. What is MediatR?

MediatR is a .NET library that implements the mediator pattern. In plain terms:

Why use it? It keeps the HTTP layer decoupled from business logic: the endpoint does not need to know which handler runs or how it works. It also makes it easy to add pipeline behaviors (e.g. validation, logging) that run before or after every request. In this project we use MediatR to wire commands and queries to their handlers and to run validators before the handler.

4. Why They Work Well Together

So: one slice = one use case = one command or one query + its handler.

5. Simple Diagram

CQRS diagram

Same flow for a command (e.g. POST create):

6. Comparison with Controller / Layered Architecture

How controller or layered architecture works

In a controller-based or layered setup you organize by technical role:

So for one feature (e.g. "create a todo") the code lives in several places: the controller action, the service method, the repository method, and maybe DTOs in a shared folder. To understand or change "create todo" you open multiple files across different layers.

How vertical slice + CQRS is different

Here you organize by use case, not by role:

Difference in one line: In layered you ask "which controller, which service, which repository?"; in vertical slice you ask "which slice?" and everything for that use case is there.

Quick comparison table

AspectLayered (horizontal)Vertical Slice + CQRS (this project)
OrganizationBy technical role (Controller, Service, Repository)By feature/use case (CreateTodo, GetTodo, …)
Adding a featureOften touch Controller, Service, maybe Repository, DTOsAdd one folder (e.g. CreateTodo) with command, handler, endpoint
Finding code“Create todo” logic spread across layers“Create todo” in Features/Todos/CreateTodo/
Read vs writeOften same service/repository for bothCommands vs queries and handlers separated (CQRS)
Best forSimple CRUD, small teams, straightforward domainsGrowing APIs, many features, teams that like feature-based structure

Both are valid. Layered is simpler for very small apps; vertical slice + CQRS keeps each use case in one place and scales better when you have many features.

Summary

Use this approach when the benefits of clear structure and CQRS matter; skip it or simplify when the project is very small or the team prefers fewer concepts and files.

Code: https://github.com/RikamPalkar/vertical-cqrs-architecture-dotnet/tree/main