Vertical Slice vs. Clean Architecture: Which Should You Choose?
I am hands-on Software Architect and Tech Lead with over 17 years of experience designing, developing and deploying various commercial software architectures on the full stack.
Search for a command to run...
I am hands-on Software Architect and Tech Lead with over 17 years of experience designing, developing and deploying various commercial software architectures on the full stack.
No comments yet. Be the first to comment.
Most teams make their most critical architectural decisions during the first few weeks of a project. That's also when collective knowledge about the domain is at its absolute lowest. It's the Project
You've decided on Vertical Slice Architecture. Now comes the question that trips up every team: how do you actually organize the folders? I've tried all the common approaches. Some blew up at scale. Others worked fine until someone tried to extract a...
I built this template because I was tired of creating the same boilerplate every time I started a new project. After writing about Vertical Slice Architecture and seeing the pattern gain traction, I w
Are you a .NET developer working on macOS, striving for high-quality code? If so, NDepend is one tool you should not overlook. In this post, we'll cover everything from setting it up to running your first analysis. Let's get started! My recent explor...
Project structure shapes everything: onboarding speed, maintainability, how fast you ship features. For years, Clean Architecture (and its cousins Onion and Hexagonal) was the default. But Vertical Slice Architecture (VSA) has become a serious alternative, and I've watched teams argue about this more than almost anything else.
This comparison applies equally to Onion and Hexagonal architectures, which share Clean Architecture's layered philosophy. The feature-based vs layered architecture debate comes down to where you want your coupling.
So which one fits your project? Let's break it down.
This article is part of my Complete Guide to Vertical Slice Architecture in .NET, which includes a production-ready template with 600+ GitHub stars-featured in NDepend's architecture comparison.
Real-world comparison: This article compares two production-ready templates—Vertical Slice Architecture template for .NET 10 (600+ stars, healthcare domain) and Jason Taylor's Clean Architecture template (17k+ stars, ToDo). All code examples are from these actual codebases.
Clean Architecture (CA), popularized by Robert C. Martin, organizes code through concentric layers. The key rule: all dependencies point inward toward the core.
Domain (Core): Business rules and entities live here. This layer depends on nothing else, so database or UI changes can't touch it.
Application (Use Cases): Application-specific business logic.
Infrastructure/UI (Outer Rings): The messy stuff: databases, web frameworks, external APIs.
The goal? Keep business logic independent from frameworks and tools. In theory, you could swap your database with minimal pain.
Vertical Slice Architecture (VSA), coined by Jimmy Bogard, flips the script. Instead of organizing by technical layer, you organize by feature.
A "slice" cuts through the entire stack - from API endpoint to database for one specific use case. The guiding principle: minimize coupling between slices, maximize cohesion within a slice.
When you add a feature in VSA, you're not scattering changes across layers. You're working in one folder that owns everything for that request.
Clean Architecture organizes by type. You get a folder for controllers, another for services, a project for repositories. Classic technical layering.
VSA organizes by capability. Got an "Order Cancellation" feature? There's an OrderCancellation folder with the endpoint, request model, handler, and data access all together. Some call this "Screaming Architecture" because your folder structure tells you what the system does.
Clean Architecture keeps technical layers loosely coupled. But here's the trade-off: code that changes together (a feature's UI and its database schema) ends up scattered across multiple projects.
VSA takes the opposite bet, following what's sometimes called the code locality principle: artifacts that change together should be grouped together. The API, logic, and database for a feature are naturally coupled—they change together. So keep them together. Less jumping between files to understand what's happening.
Let's trace what happens when you need to add a field to a feature in both architectures. I'll use real examples from production templates.
In Clean Architecture (Jason Taylor's template):
To add a "Tags" field to the TodoItem feature, you touch 8-10 files across 4 projects:
Domain/Entities/TodoItem.cs — Add property to entity
Application/TodoItems/Commands/CreateTodoItem/CreateTodoItem.cs — Add to command
Application/TodoItems/Commands/CreateTodoItem/CreateTodoItemCommandValidator.cs — Add validation rule
Application/TodoItems/Commands/UpdateTodoItemDetail/UpdateTodoItemDetail.cs — Add to update command
Application/TodoItems/Queries/GetTodoItemsWithPagination/TodoItemBriefDto.cs — Add to list DTO
Application/TodoLists/Queries/GetTodos/TodoItemDto.cs — Add to detail DTO
Infrastructure/Data/Configurations/TodoItemConfiguration.cs — Add EF Core mapping
Web/Endpoints/TodoItems.cs — Update endpoint mappings (if needed)
Plus corresponding test files in tests/Application.FunctionalTests/TodoItems/
You also need to rebuild 4 separate projects (Domain, Application, Infrastructure, Web) for the change to compile.
In VSA (my template):
To add a "CancellationCategory" enum field to the CancelAppointment feature, you touch 2-3 files:
Application/Domain/Appointment.cs — Add property and update domain method
Application/Scheduling/CancelAppointment.cs — Update command, validator, handler
Infrastructure/Persistence/Configurations/AppointmentConfiguration.cs — Add EF mapping (if needed)
// In Appointment.cs - Add property
public CancellationCategory? Category { get; private set; }
// Update Cancel method signature
public void Cancel(string reason, CancellationCategory category, DateTime? cancelledAtUtc = null)
{
// ... existing validation ...
Category = category;
}
// In CancelAppointment.cs - Update command
public record Command(Guid AppointmentId, string Reason, CancellationCategory Category);
// Add validation
RuleFor(v => v.Category).IsInEnum();
// Use in handler
appointment.Cancel(request.Reason, request.Category);
// In AppointmentConfiguration.cs (optional - only if custom mapping needed)
builder.Property(a => a.Category).HasConversion<int>();
The entire request/response flow lives in one cohesive area. Domain model, slice logic, and persistence config all sit in the Application project.
Change impact: CA scatters changes across layers; VSA contains them in one folder.
Clean Architecture is mock-heavy. Every layer hides behind interfaces, so your unit tests often end up testing mocks of the layer below. You're testing the wiring, not the behavior.
VSA leans toward integration testing. A slice is a self-contained request/response, so the most useful test exercises the full slice from API to database. Save unit tests for the shared domain model, not the handlers.
Why the difference? In Clean Architecture, the layering makes it natural to test each layer in isolation. In VSA, the handler is so thin (mostly orchestration) that integration tests give you more bang for your buck. You save unit tests for the parts with complex logic domain and validators.
You need to ship fast. Features live in isolation, so you can move quickly without stepping on other code.
Requirements keep changing. Throwing away a self-contained slice is easier than untangling logic spread across layers.
Your team is experienced. VSA has fewer guardrails. You need developers who'll recognize when to extract shared domain logic instead of copy-pasting.
You're building APIs. Request-response systems map naturally to slices.
You have complex business rules. The protected domain core gives you a place for rich logic that won't get polluted by infrastructure concerns.
You actually might swap infrastructure. If there's a real chance you'll change databases or frameworks, CA's abstraction boundaries pay off.
Your team is newer. The rigid "Controller calls Service calls Repository" structure acts as training wheels. It's harder to make a mess.
Here's what I've seen work in practice: organize your top-level structure by features (VSA), but inside complex slices, apply CA principles.
Feature folders at the macro level. Each slice owns its endpoint, handler, and simple data access.
Domain separation at the micro level. When a slice gets complex, extract domain logic into a separate class.
Shared domain model. Multiple slices can reference common entities and value objects that stay persistence-ignorant.
You don't have to pick one religion. Use the structure that fits the complexity at hand.
For detailed folder organization patterns, see VSA Folder Structure: 4 Approaches Compared.
If your CA project has turned into "lasagna architecture" with too many layers, you can simplify through defactoring:
Inline the abstractions. Move repository and service logic back into the handler for a single feature.
Group by feature. Put the endpoint, command, handler, and validator in one folder.
Delete the interfaces. If an interface only exists to satisfy the layering, remove it.
This feels wrong at first. You've been taught that more abstraction is better. But if an abstraction doesn't have multiple implementations and doesn't enable testing, it's just noise.
"But what about logging? Validation? Transactions?"
Use a mediator pipeline (MediatR, Wolverine). Each slice stays simple while behaviors handle the cross-cutting stuff in one place. You get consistency without polluting every handler.
For implementation details, see Vertical Slice Architecture template for .NET 10.
Junior-heavy teams: Lean toward Clean Architecture. The prescriptive rules ("Controller calls Service") keep code organized while people learn. The structure does some of the thinking for them.
Experienced teams: VSA gives you speed. Senior developers have the judgment to refactor procedural code into a rich domain model and abstractions when it makes sense, and to leave simple CRUD operations simple.
| If your project is... | Consider... | Example Template |
|---|---|---|
| Primarily CRUD / Data-driven | VSA or simple N-Tier | VSA template for .NET 10 |
| Rich, complex business rules | Clean Architecture | Jason Taylor's Clean Architecture |
| Rapidly changing features | VSA | VSA template for .NET 10 |
| Long-term (5+ years) maintanance | Hybrid approach | Both patterns can evolve |
| Needs independent scalability, headed toward microservices | VSA (slices become services) | Vertical slices map to service boundaries |
There's no universal winner. Clean Architecture protects complexity but adds ceremony. VSA reduces ceremony but requires discipline.
Start simple. If you're building a straightforward APIs, VSA will get you moving faster. If you're modeling a complex domain with lots of business rules, Clean Architecture's protected core is worth the overhead.
And don't be afraid to mix them. The best architectures I've seen treat this as a spectrum, not a binary choice.
New to the template? Start with the Quick Start Guide to get the healthcare API running in minutes.
Deciding on folder organization? See VSA Folder Structure: 4 Approaches Compared for practical patterns.
Ready to try VSA? Clone the Vertical Slice Architecture template (600+ stars) — a production-ready starting point with healthcare domain examples.
Want the full picture? Read the Complete Guide to Vertical Slice Architecture for implementation details, MediatR patterns, and testing strategies.