When to Choose Vertical Slice Architecture Over Layered Architecture

dotnetsoftware-architecturevertical-slice-architecture
When to Choose Vertical Slice Architecture Over Layered Architecture

Choose Vertical Slice Architecture when your features are mostly independent, the application is CRUD-heavy, you want fast iteration with minimal ceremony, or your team works on many features in parallel. Layers still win when many features share complex business logic or you need strict compile-time boundaries. Here are the concrete signals for each, and what to do when they point both ways.

Layered architecture is the default choice in .NET, and defaults rarely get questioned.

The Problem With Layers

In a layered architecture, a simple "Get Order by ID" feature touches:

  1. OrdersController (Presentation)
  2. IOrderService + OrderService (Application)
  3. IOrderRepository + OrderRepository (Infrastructure)
  4. OrderDto, OrderResponse (Mapping)

Seven types across four layers for a database query that returns one object. Adding a new feature means touching every layer, and modifying one means jumping between folders.

What Changes Together Should Live Together

Vertical Slice Architecture organizes code by feature instead of layer, and it's easier to adopt than most people think:

// Layered: related code is scattered
Controllers/OrdersController.cs    ← GetOrder, CreateOrder, DeleteOrder
Services/OrderService.cs           ← GetOrder, CreateOrder, DeleteOrder
Repositories/OrderRepository.cs    ← GetOrder, CreateOrder, DeleteOrder

// VSA: related code is co-located
Features/Orders/GetOrder.cs        ← everything for GetOrder
Features/Orders/CreateOrder.cs     ← everything for CreateOrder
Features/Orders/DeleteOrder.cs     ← everything for DeleteOrder

When you work on "Get Order," you open one file. When you review a pull request, the diff shows one file per feature.

When to Choose VSA

Your Features Are Independent

If most features don't share logic, VSA reduces unnecessary abstractions:

// This feature needs no repository, no service layer
public static class GetOrder
{
    public sealed record Query(Guid Id);

    public sealed record OrderResponse(
        Guid Id, string Status, decimal TotalAmount, DateTime CreatedAt);

    public sealed class Handler(ApplicationDbContext db)
    {
        public async Task<OrderResponse?> Handle(
            Query query, CancellationToken ct)
        {
            return await db.Orders
                .Where(o => o.Id == query.Id)
                .Select(o => new OrderResponse(
                    o.Id, o.Status, o.TotalAmount, o.CreatedAt))
                .FirstOrDefaultAsync(ct);
        }
    }
}

The handler queries ApplicationDbContext directly and projects the result into OrderResponse.

Your Team Is Growing

With layers, two developers working on separate features often edit the same controller and the same service, so merge conflicts are frequent.

With VSA, each developer works in separate files. Feature A doesn't touch Feature B's code.

You Want Fast Iteration

VSA has less ceremony. Adding a new feature means creating one file that defines the request, handler, and endpoint. You don't define an interface or implement a repository, and assembly scanning handles the registration.

Your Application Is CRUD-Heavy

Many business applications are variations of create-read-update-delete. VSA handles this cleanly:

Features/
  Products/
    CreateProduct.cs      ← 60 lines
    GetProduct.cs         ← 40 lines
    GetProducts.cs        ← 50 lines
    UpdateProduct.cs      ← 70 lines
    DeleteProduct.cs      ← 30 lines

Each file is small and self-contained.

You're Using CQRS

CQRS and VSA are a natural pair. Commands and queries are already separate operations, so each one can go in its own file:

Features/
  Orders/
    Commands/
      PlaceOrder.cs
      CancelOrder.cs
    Queries/
      GetOrder.cs
      GetOrders.cs

When to Choose Layers

You Have Complex Shared Business Logic

If 10 features all need the same pricing calculation, a PricingService in a service layer makes sense. Duplicating that logic across 10 slices is worse.

You Need Strict Architectural Boundaries

Layers in separate projects enforce compile-time boundaries through project references. A domain project that doesn't reference the data access project cannot use its DbContext. VSA in a single project doesn't stop a handler from referencing anything else in the project.

Your Team Is Familiar With Layers

If your team knows layered architecture well and productivity is good, switching to VSA for the sake of switching creates churn without value.

You Have a Rich Domain Model

Domain-Driven Design with a rich domain model benefits from a dedicated domain layer. The domain layer contains complex business rules that multiple features share. Clean Architecture is a better fit here.

The Middle Ground

You don't have to be all-in on either approach. This layout combines both:

src/
  MyApp.Api/
    Features/           ← Vertical slices for individual operations
      Orders/
        PlaceOrder.cs
        GetOrder.cs
    Domain/             ← Shared domain entities (from Clean Architecture)
      Order.cs
      Customer.cs
    Shared/             ← Cross-cutting concerns
      Behaviors/
        ValidationBehavior.cs

Use slices for application logic, a domain layer for shared business rules, and pipeline behaviors for cross-cutting concerns.

Migrating From Layers, Incrementally

Choosing VSA doesn't mean rewriting your layered application. The migration path I recommend:

  1. New features go in a Features folder as self-contained slices. Don't touch the existing layers yet.
  2. When you modify an existing feature, consider moving it into a slice as part of the change. The controller action, service method, and repository method collapse into one handler.
  3. Leave stable code alone. A feature nobody has touched in a year gains nothing from being restructured.
  4. Delete layers as they empty out. When OrderService has one method left, inline it and remove the class.

After a few months you have a codebase that's mostly slices with a small legacy core, and you got there without a big-bang rewrite or a feature freeze.

Decision Matrix

A decision flow: rich shared domain logic or strict compile-time boundaries point to layered or Clean Architecture; independent, CRUD-heavy, or CQRS features point to vertical slices, otherwise a hybrid

Signals that point toward Vertical Slice Architecture:

  • Features are largely independent of each other
  • The application is CRUD-heavy
  • You need fast iteration with minimal ceremony
  • The team is growing and works on many features in parallel
  • You're already using CQRS

Signals that point toward layers:

  • Heavy shared business logic across features
  • A rich domain model with DDD
  • You need strict compile-time boundaries
  • The team is experienced and productive with layers

If your signals land on both sides, that's normal. It usually means the hybrid approach (slices plus a shared domain layer) is your answer.

Summary

Choose Vertical Slice Architecture when:

  1. Features are independent and rarely share logic
  2. Your team needs parallel development without merge conflicts
  3. You value simplicity: one file per feature, no unnecessary abstractions
  4. You're already using CQRS (with or without MediatR)
  5. The application is CRUD-heavy without complex domain logic

Choose layers when shared business logic, strict boundaries, or DDD richness justifies the overhead.

Thanks for reading, and stay awesome!


Frequently Asked Questions

When should you use Vertical Slice Architecture?

When features are mostly independent, the application is CRUD-heavy or varied in complexity, you want fast iteration with minimal ceremony, and your team benefits from working in parallel without merge conflicts. CQRS-based apps map onto slices especially well.

When is layered architecture the better choice?

When many features share complex business logic, you need compile-time boundaries between layers, or you are building a rich domain model with DDD. A dedicated domain layer earns its keep when rules are shared across many use cases.

Is Vertical Slice Architecture good for large projects?

Yes, if you group slices by domain area as the feature count grows. Large codebases often combine slices for application logic with a shared domain layer for business rules.

Does Vertical Slice Architecture work without MediatR?

Yes. The pattern organizes code by feature and does not depend on any library. Minimal API endpoints calling plain handler classes achieve the same result.

Browse all Vertical Slice Architecture guides →
Loading comments...

Whenever you're ready, there are 4 ways I can help you:

  1. Pragmatic Clean Architecture: Join 5,000+ students in this comprehensive course that will teach you the system I use to ship production-ready applications using Clean Architecture. Learn how to apply the best practices of modern software architecture.
  2. Modular Monolith Architecture: Join 2,800+ engineers in this in-depth course that will transform the way you build modern systems. You will learn the best practices for applying the Modular Monolith architecture in a real-world scenario.
  3. Pragmatic REST APIs: Join 1,900+ students in this course that will teach you how to build production-ready REST APIs using the latest ASP.NET Core features and best practices. It includes a fully functional UI application that we'll integrate with the REST API.
  4. Patreon Community: Join a community of 5,000+ engineers and software architects. You will also unlock access to the source code I use in my YouTube videos, early access to future videos, and exclusive discounts for my courses.

The .NET Weekly

Become a Better .NET Software Engineer

Join 66,000+ engineers who are improving their skills every Saturday morning.