# When to Choose Vertical Slice Architecture Over Layered Architecture

> Four files across four folders for a query that returns one object: that is the layered tax on every feature. Sometimes the tax buys you real structure, and sometimes it buys you nothing. Here are the concrete signals that tell you when Vertical Slice Architecture is the better fit for your .NET project, and when layers still win.

Published: 2026-08-13. Author: Milan Jovanović.

Canonical: https://milanjovanovic.tech/blog/when-to-choose-vertical-slice-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.
But an architecture you picked by inertia is still an architecture decision, just one made without looking at the alternatives.

## 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)

Four files across four folders for a database query that returns one object. Adding a new feature means touching every layer. Modifying a feature means jumping between folders.

## What Changes Together Should Live Together

[**Vertical Slice Architecture**](https://milanjovanovic.tech/blog/vertical-slice-architecture-dotnet) organizes code by feature instead of layer, and it's [**easier to adopt than most people think**](https://milanjovanovic.tech/blog/vertical-slice-architecture-is-easier-than-you-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:

```csharp
// 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);
        }
    }
}
```

No interface, no repository, no service. Just a query that returns data.

### Your Team Is Growing

With layers, two developers working on separate features often edit the same files - the same controller, the same service. Merge conflicts happen frequently.

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:

1. Create one file
2. Define the request, handler, and endpoint
3. Done

No interface to define, no repository to implement, 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. No layered abstractions adding complexity without value.

### You're Using CQRS

[**CQRS**](https://milanjovanovic.tech/blog/cqrs-pattern-the-way-it-should-have-been-from-the-start) and VSA are a natural pair. Commands and queries are already separate operations - putting each in its own file is the logical next step:

```
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 enforce compile-time boundaries. The presentation layer physically cannot reference the database. VSA in a single project doesn't prevent a handler from doing whatever it wants.

### Your Team Is Familiar With Layers

Architecture decisions are team decisions. 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**](https://milanjovanovic.tech/blog/clean-architecture-vs-onion-vs-hexagonal) is a better fit here.

## The Middle Ground

You don't have to be all-in on either approach. Many successful projects combine 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. Use a domain layer for shared business rules. Use 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](https://milanjovanovic.tech/blogs/articles/when-to-choose-vertical-slice-architecture/architecture-decision.png)

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**](https://milanjovanovic.tech/blog/vertical-slice-architecture-dotnet) 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.

The best architecture is the one your team can maintain and evolve. Start with what fits your problem, not what's trending.

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 is about organizing code by feature, not about any library. Minimal API endpoints calling plain handler classes achieve the same result.
