# Vertical Slice Architecture Folder Structure: From 5 to 50+ Features

> A concrete folder and solution layout for Vertical Slice Architecture in .NET, and how it evolves as you grow from 5 features to 50+.

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

Canonical: https://milanjovanovic.tech/blog/vertical-slice-project-structure-dotnet

Structure a Vertical Slice Architecture project around a `Features` folder with one file per use case, plus a `Domain` folder for shared entities, `Data` for EF Core infrastructure, and `Shared` for cross-cutting behaviors.
One project is enough to start, and grouping by domain area keeps the layout manageable past 50 features.

Vertical Slice Architecture sounds simple until you create the solution and have to decide where everything goes.
Where do domain entities live?
Does the DbContext get its own project?
This article answers those questions with a concrete layout, and shows how it evolves as the project grows.

## The Problem With Layered Folders

In a traditional layered project, you get folders like this:

```
Controllers/
  OrdersController.cs
  CustomersController.cs
  ProductsController.cs
Services/
  OrderService.cs
  CustomerService.cs
  ProductService.cs
Repositories/
  OrderRepository.cs
  CustomerRepository.cs
  ProductRepository.cs
Models/
  Order.cs
  Customer.cs
  Product.cs
```

To understand how "Place Order" works, you jump between 4+ folders. Adding a feature means touching multiple folders. Related code is scattered.

[**Vertical Slice Architecture**](https://milanjovanovic.tech/blog/vertical-slice-architecture-dotnet) fixes this by organizing code around features. This article is about the practical part: the actual folder and solution layout, and how it holds up as the feature count grows.

For what goes **inside** a slice (the command, handler, and validator structure), see my newsletter issue on [**structuring vertical slices**](https://milanjovanovic.tech/blog/vertical-slice-architecture-structuring-vertical-slices). Here we stay at the folder level.

## The Starting Layout: 5-15 Features

One project, one `Features` folder, one file per use case:

```
Features/
  Orders/
    PlaceOrder.cs
    GetOrder.cs
    GetOrders.cs
    CancelOrder.cs
    OrdersModule.cs
  Customers/
    RegisterCustomer.cs
    GetCustomer.cs
    UpdateCustomer.cs
    CustomersModule.cs
  Products/
    CreateProduct.cs
    GetProducts.cs
    SearchProducts.cs
    ProductsModule.cs
```

Everything for "Place Order" is in one file. Everything for orders is in one folder. The `*Module.cs` file per folder registers that feature group's endpoints (a [**Carter**](https://milanjovanovic.tech/blog/vertical-slice-architecture-carter-dotnet) module or a plain extension method, your choice).

Two naming rules keep this navigable:

- **Files are verbs**: `PlaceOrder.cs`, not `OrderService.cs`. The folder listing reads like a feature list.
- **One use case per file**: if a file handles two operations, it's two slices pretending to be one.

## Full Project Structure

Here's the complete single-project layout I use:

```
src/
  MyApp.Api/
    Features/
      Orders/
        PlaceOrder.cs
        GetOrder.cs
        GetOrders.cs
        CancelOrder.cs
        UpdateOrderStatus.cs
        OrdersModule.cs
      Customers/
        RegisterCustomer.cs
        GetCustomer.cs
        GetCustomers.cs
        UpdateCustomer.cs
        CustomersModule.cs
      Products/
        CreateProduct.cs
        GetProducts.cs
        SearchProducts.cs
        ProductsModule.cs
    Domain/
      Order.cs
      Customer.cs
      Product.cs
      Common/
        Entity.cs
        Result.cs
        Error.cs
    Data/
      ApplicationDbContext.cs
      Configurations/
        OrderConfiguration.cs
        CustomerConfiguration.cs
        ProductConfiguration.cs
      Migrations/
    Shared/
      Behaviors/
        ValidationBehavior.cs
        LoggingBehavior.cs
      Middleware/
        ExceptionHandlingMiddleware.cs
    Program.cs
tests/
  MyApp.Api.Tests/
    Features/
      Orders/
        PlaceOrderTests.cs
        GetOrderTests.cs
      Customers/
        RegisterCustomerTests.cs
```

Note that the test project mirrors the `Features` tree exactly. Finding the tests for a slice should never require a search.

## Key Decisions

### One Project or Multiple?

**Single project** - the default for VSA. Keep it simple:

```
MyApp.Api/
  Features/
  Domain/
  Data/
  Shared/
```

**Multiple projects** - only when you need strict compile-time enforcement:

```
MyApp.Api/          ← entry point
MyApp.Features/     ← all features
MyApp.Domain/       ← domain entities
MyApp.Data/         ← EF Core, migrations
```

Start with one project. Split when you have a reason. Folder boundaries are cheap to change; project boundaries are not. If you want boundary enforcement without extra projects, **architecture tests** on namespaces get you most of the way.

### Where Do Domain Entities Live?

In a `Domain/` folder within the same project:

```
Domain/
  Order.cs
  LineItem.cs
  Customer.cs
  Product.cs
  Common/
    Entity.cs
    AggregateRoot.cs
    IDomainEvent.cs
```

Domain entities are shared across features. An `Order` entity is used by `PlaceOrder`, `GetOrder`, and `CancelOrder`. Slices own their request and response types; they share the domain model underneath.

### Where Does the DbContext Live?

In a `Data/` folder:

```
Data/
  ApplicationDbContext.cs
  Configurations/
    OrderConfiguration.cs
    CustomerConfiguration.cs
  Migrations/
```

EF Core configurations are separate from features - they're infrastructure, not business logic.

### Shared Code (Cross-Cutting Concerns)

Pipeline behaviors, middleware, and shared abstractions in `Shared/`:

```
Shared/
  Behaviors/
    ValidationBehavior.cs
    LoggingBehavior.cs
    CachingBehavior.cs
  Middleware/
    ExceptionHandlingMiddleware.cs
    RequestLoggingMiddleware.cs
  Abstractions/
    ICommand.cs
    IQuery.cs
    ICacheable.cs
  Extensions/
    ResultExtensions.cs
```

Keep this folder small and boring. If `Shared` starts accumulating business logic, a slice boundary is leaking; see [**cross-cutting concerns in Vertical Slice Architecture**](https://milanjovanovic.tech/blog/cross-cutting-concerns-in-vertical-slice-architecture) for what belongs here and what doesn't.

## When Features Get Complex

A simple feature fits in one file. A complex feature graduates to a folder:

```
Features/
  Orders/
    PlaceOrder/
      PlaceOrderCommand.cs
      PlaceOrderHandler.cs
      PlaceOrderValidator.cs
      PlaceOrderResponse.cs
    GetOrder/
      GetOrderQuery.cs
      GetOrderHandler.cs
    Shared/
      OrderResponse.cs
    OrdersModule.cs
```

My threshold: split into a folder when the single file grows past roughly 150-200 lines, or when a slice needs private helper classes that would pollute the file.

A feature-local `Shared/` folder (like `Orders/Shared/`) is fine for DTOs reused by two or three sibling slices, like the `OrderResponse` that both `GetOrder` and `GetOrders` return. It's still inside the feature boundary, which is very different from a global shared folder.

## Scaling to 50+ Features

A flat `Features` folder stops working around a few dozen slices. The fix is one more level: group by domain area.

![The folder structure evolves from a flat Features folder at 5-15 features, to domain-area grouping at 50+ features, to a modular monolith where each area becomes a module](https://milanjovanovic.tech/blogs/articles/vertical-slice-project-structure-dotnet/structure-evolution.png)


```
Features/
  Ordering/
    PlaceOrder.cs
    GetOrder.cs
    CancelOrder.cs
    OrderingModule.cs
  Catalog/
    CreateProduct.cs
    SearchProducts.cs
    CatalogModule.cs
  Identity/
    RegisterUser.cs
    Login.cs
    RefreshToken.cs
    IdentityModule.cs
  Shipping/
    CreateShipment.cs
    TrackShipment.cs
    ShippingModule.cs
```

These groups aren't arbitrary. They mirror [**bounded contexts**](https://milanjovanovic.tech/blog/bounded-context-ddd-explained), and each one is a candidate module if you later evolve toward a [**modular monolith**](https://milanjovanovic.tech/blog/modular-monolith-architecture-dotnet). At that point each domain area gets its own project (or set of projects), and the folder structure you already have becomes the module structure. I've written about exactly [**where vertical slices fit inside a modular monolith**](https://milanjovanovic.tech/blog/where-vertical-slices-fit-inside-the-modular-monolith-architecture).

The practical signals that you've hit this stage:

- You scroll to find anything in `Features/`
- Two domain areas keep reaching into each other's entities
- Different teams own different feature groups and step on each other in PRs

Restructuring is mechanical: create the domain-area folders, move files, fix namespaces. Do it in one PR before the pain compounds.

## Conventions That Keep the Structure Healthy

A folder structure only stays clean if a few conventions back it up:

- **Namespace mirrors folder.** `MyApp.Api.Features.Ordering.PlaceOrder` tells you exactly where the file lives. Most IDEs enforce this automatically.
- **Handlers are `internal`.** Nothing outside the slice should call a handler directly. The endpoint (or dispatcher) is the only entry point.
- **One route prefix per module.** `OrdersModule` owns `/api/orders`; no other module maps routes under it.
- **No slice-to-slice references.** If `PlaceOrder` needs something from `Shipping`, that's a domain service or a domain event, not a `using` statement. A couple of [**architecture tests**](https://milanjovanovic.tech/blog/5-architecture-tests-you-should-add-to-your-dotnet-projects) will hold this line for you.

## Summary

A practical VSA folder structure:

1. **Features folder** - one file per use case, named as a verb
2. **Domain folder** - shared entities and value objects
3. **Data folder** - DbContext, configurations, migrations
4. **Shared folder** - pipeline behaviors, middleware, abstractions (keep it boring)
5. **Single project** to start, split only when you need compile-time boundaries
6. **Single-file slices** first, folders past ~150-200 lines
7. **Domain-area grouping** at 50+ features, mirroring bounded contexts

The structure should make it obvious what your application does by reading the feature folder names.

Thanks for reading, and stay awesome!

---

## Frequently asked questions

### How do you structure a Vertical Slice Architecture project?

Organize a Features folder with one file or subfolder per use case, grouped by domain area. Keep shared domain entities in a Domain folder, EF Core infrastructure in Data, and cross-cutting behaviors in Shared. One project is enough to start.

### Should Vertical Slice Architecture use one project or multiple?

Start with a single project. Multiple projects only add value when you need compile-time enforcement of boundaries, which usually matters once teams and feature counts grow or when you evolve toward a modular monolith.

### Where do domain entities live in Vertical Slice Architecture?

In a shared Domain folder, because entities like Order are used by several slices (PlaceOrder, GetOrder, CancelOrder). Slices own their request, handler, and response; they share the domain model underneath.

### How do you keep a Features folder manageable with 50+ features?

Introduce a domain-area level: Features/Ordering, Features/Catalog, Features/Identity, each containing its use cases and endpoint module. The groups mirror bounded contexts and give you a natural path to a modular monolith.
