Back to Blog
#architecture#dotnet#vertical-slice

Vertical Slice vs Layered Architecture in .NET

3 min read

A pragmatic comparison of feature-first and layer-first organization in a .NET 8 microservices ecosystem.

Two different ways to organize change

Layered architecture organizes code by technical responsibility. You often see folders such as Pages, Models, Services, Controllers, and Data. This works well when a service has a clear horizontal flow and a small number of business behaviors.

Vertical Slice Architecture organizes code by feature. Instead of jumping across many technical folders, a developer can work inside one slice that contains the endpoint, validation, request handling, persistence logic, and response model for a business action.

Where layered architecture fits

Shopping.Web is a good example of a layered shape. Razor Pages, View Components, models, and service classes are easy to understand when the service is mainly responsible for presentation and downstream API consumption.

Discount.Grpc can also be read in layers. The .proto contract, generated stubs, service implementation, and storage logic form a simple technical flow. A full feature-slice structure may add more ceremony than value when the service boundary is narrow.

Layered architecture is not bad by default. It becomes risky when business rules are scattered across controllers, services, repositories, and database code with no clear ownership.

Where vertical slices fit

Catalog and Basket are better candidates for Vertical Slice Architecture because features can change independently. A CreateProduct, GetProduct, AddItemToBasket, or CheckoutBasket flow can keep its request, handler, validation, and persistence behavior close together.

This lowers navigation cost. A developer investigating one feature does not need to open five shared folders before understanding the request path.

Vertical slices pair naturally with CQRS and MediatR. Commands change state. Queries read data. Each slice can own only the dependencies it needs.

Basket and the decorator example

Basket also shows why feature organization still needs engineering discipline. The repository abstraction can have a concrete database implementation and a cached decorator such as CachedBasketRepository.

That gives the service a clean way to add Redis caching without spreading cache logic across every handler.

  • IBasketRepository defines the behavior the feature needs.
  • BasketRepository handles the primary persistence path.
  • CachedBasketRepository adds Redis behavior around the original repository.
  • Handlers depend on the abstraction instead of knowing the cache details.

Trade-offs to watch

Vertical Slice Architecture can reduce shared-layer friction, but it can also create duplication. Some duplication is healthy because each feature is explicit. Too much duplication means the team needs shared conventions or small reusable helpers.

Layered architecture can be simple, but it can also hide business coupling. If every feature depends on the same large service class, one change can create unexpected regression risk across the system.

The architectural decision should follow change patterns, not fashion.

How to choose pragmatically

Use layered architecture when the service is small, technically straightforward, or mostly presentation and integration logic. Use Vertical Slice Architecture when features are the primary unit of change and each use case benefits from local ownership.

For a .NET 8 microservices system, mixing both is acceptable. The technical lead's job is to keep each service internally consistent, testable, and honest about its operational cost.