Vertical Slice Architecture
What Vertical Slice Architecture is, why it organises code by request instead of by layer, and how to keep slices from turning into copy-paste. For architects weighing it against Clean Architecture or combining the two.
- Researched
- 15 Oct 2026
- Reading time
- 6 min
- Sources
- 15
- Review due
- 15 Oct 2027
- Organise by request, not by layer: each feature's endpoint, validation, logic and data access live together.
- Core principle: minimise coupling between slices and maximise coupling within a slice (Jimmy Bogard).
- Change stays local: a new feature adds code in one place instead of touching every layer.
- It trades structure for skill: without layers to guide them, teams need refactoring discipline and mechanical guardrails.
- Combines well with Clean: slices organise behaviour, a shared domain and the dependency rule protect the core.
On this page 9 sections
The idea in one minute
Vertical Slice Architecture organises code around individual requests: “validate a configuration”, “apply a configuration”, “register a device”. Everything one request needs sits together: its endpoint, request and response types, validation, handler and the data access only it uses. Jimmy Bogard summarises the principle as “minimize coupling between slices, and maximize coupling in a slice.”[1]
The argument is about where change happens. A new feature in a layered design touches a controller, a service, a repository and their interfaces, each in a different project. In a sliced design the same feature adds one folder. Bogard calls this coupling “along the axis of change”.[1]
When it fits
| Fits when… | Doesn’t fit when… |
|---|---|
| Features are mostly independent requests (typical web APIs, admin and CRUD-heavy tools) | Complex rules are shared by many features |
| Several developers add features in parallel and fight merge conflicts in shared layers | You need compile-time boundaries; one project cannot stop a handler from doing anything |
| You already think in commands and queries (CQRS) | A rich domain model is the heart of the system |
| The team is comfortable refactoring and spotting code smells | The team is new and relies on layers as guidance |
These criteria follow Jovanović’s 2026 assessment,[9] and Bogard’s own precondition: the approach assumes the team understands code smells and refactoring.[1]
How a slice is built
- One folder (or one file) per request. Bogard’s reference app keeps the query, validator, command, mapping and handlers of one page in a single file.[5]
- Each slice chooses its own approach. Start with a simple transaction script; refactor to domain objects when smells appear. New features only add code, they do not change shared code.[1]
- Commands and queries fall out naturally, which is how CQRS arrived “out of the gate” in Bogard’s own move away from onion architecture.[1]
- A mediator is optional. MediatR is common in .NET samples, but became commercial in 2025 for companies above a revenue threshold;[6] plain handlers or endpoint libraries (REPR: request, endpoint, response) work just as well.[12]
Where shared logic goes
Jovanović separates three kinds of sharing:[8]
| Kind | Where it goes |
|---|---|
| Technical infrastructure (logging, results, pipelines) | Shared freely |
| Domain rules (invariants, calculations) | Pushed into entities and value objects |
| Logic shared by a few related slices | A local Shared folder inside that feature area |
His heuristics: one duplicate is fine, three should raise an eyebrow, and duplication is cheaper than the wrong abstraction.[8]
Worked example: a configuration tool
An illustrative layout. Slices own requests; the domain model holds rules many slices need.
src/ConfigTool.Api/
Features/
Configurations/
ValidateConfiguration/
Request.cs // record: DeviceId, Payload
Validator.cs // shape and schema checks
Handler.cs // loads the device profile, asks the domain
Endpoint.cs // POST /devices/{id}/config:validate
ApplyConfiguration/ ...
Shared/ // only for configuration slices
Devices/
RegisterDevice/ ...
Domain/ // DeviceProfile, ConfigRule: shared invariants
Infrastructure/ // DbContext, device protocol adapters
tests/
ConfigTool.IntegrationTests/Features/Configurations/
ConfigTool.ArchitectureTests/
public static class ValidateConfiguration
{
public sealed record Request(DeviceId DeviceId, ConfigPayload Payload);
public sealed class Handler(ConfigDb db)
{
public async Task<ValidationReport> Handle(Request req, CancellationToken ct)
{
var profile = await db.DeviceProfiles.SingleAsync(p => p.Id == req.DeviceId, ct);
return profile.Validate(req.Payload); // the rule lives on the domain object
}
}
}
Where it is used in practice
- Bogard’s reference application (Contoso University) and his talks since NDC Oslo 2015.[2][5]
- Ardalis’ Clean Architecture template ships a minimal, single-project variant organised as vertical slices next to the classic four-project layout.[12]
- SSW publishes it as a consulting rule (“features do not reference each other”)[13] and ships a .NET template with architecture tests built in.[14]
- Combined with Clean Architecture: slices organise behaviour while Clean Architecture controls dependency direction.[10] Bogard’s own session covers mixing slices with layers, CQRS and DDD.[3]
Gains and costs
What it buys
- Local change. A feature is one folder; deleting a feature is deleting a folder.
- Fewer merge conflicts. Parallel features stop meeting in shared service and repository classes.
- Right-sized code per feature. Simple requests stay simple; complex ones get domain objects.
- Natural CQRS. Commands and queries are separate slices from day one.
- Fits AI-assisted work. A slice fits in one context window and limits the blast radius of a change.
What it costs
- Needs skill. Without layers as guidance, quality depends on refactoring discipline.
- Duplication risk. Similar slices drift into copy-paste unless rules move into the domain.
- Weaker compile-time boundaries. In one project, nothing stops a handler from reaching anywhere.
- Shared rules need a home. Rule-heavy systems still need a protected core.
How it goes wrong
- The Common junk drawer. A shared
CommonorUtilsproject collects everything and brings back the coupling slices were meant to remove.[8] - Slices calling slices. Use the domain, events or a small public facade instead of reaching into another slice’s handler.[8]
- Anaemic slices. Every rule written inline in handlers, copied three times, never pushed into the domain.
- Shared DTOs. One request type reused across slices couples them through the back door.
- Abstracting too early. Generic base handlers and repositories before the third duplicate.
- Huge slices. A use case that grows until it does too much; split it like any other oversized class.[9]
Guardrails and testing
Bogard’s 2026 talk is explicit that slices remove some structural guardrails and that mechanical ones must replace team discipline: architecture tests, conventions kept in the repository, and a reference slice to copy.[4]
- Architecture tests (for example NetArchTest) enforce that features do not reference each other and that the domain does not depend on infrastructure.[15]
- Conventions in the repo: a short guide and one exemplary slice new features copy.
- Test by feature, not by layer: name tests after slices, drive them through the real HTTP boundary against a real database in a container, and unit-test only validators and complex domain logic.[11] Bogard’s reference app uses integration tests only, with a fixture that sends requests through the same pipeline as production.[5]
Origins
- 2015: Bogard’s talk “SOLID Architecture: Slices not Layers” at NDC Oslo, after moving away from onion architecture toward CQRS.[2]
- 2018: the blog post “Vertical Slice Architecture” that gave the style its name and principle.[1]
- 2021 onward: practitioners such as Derek Comartin frame it as a question of coupling and dependencies: things that change together belong together.[7]
- 2026: Bogard positions slices as guardrails for AI-assisted development, paired with architecture tests and repository conventions.[4]
Key takeaways
- Slices couple along the axis of change: things that change together live together.
- Start each slice simple (a transaction script) and push logic into the domain when it repeats; duplication is cheaper than the wrong abstraction.
- Avoid a Common junk drawer and slices calling slices; share through the domain or events instead.
- Replace the guardrails layers gave you with architecture tests, conventions in the repo and a reference slice.
- Test by feature, through the real boundary, not layer by layer with mocks.
Sources
- Vertical Slice Architecture · Jimmy Bogard, 2018Primaryjimmybogard.com
- SOLID Architecture: Slices not Layers (NDC Oslo 2015) · Talk recording recordPrimaryav.tib.eu
- Vertical Slice Architecture (session abstract) · Jimmy BogardPrimarysessionize.com
- Vertical Slice Architecture: Effective Guardrails for AI Development · Jimmy Bogard, 2026Primarysessionize.com
- ContosoUniversityDotNetCore-Pages · Bogard's reference applicationReferencegithub.com
- AutoMapper and MediatR going commercial · Jimmy Bogard, 2025Primaryjimmybogard.com
- Restructuring to a Vertical Slice Architecture · Derek Comartin, 2021Secondarycodeopinion.com
- Vertical Slice Architecture: where does the shared logic live? · Milan Jovanović, 2025Secondarymilanjovanovic.tech
- When to choose Vertical Slice Architecture · Milan Jovanović, 2026Secondarymilanjovanovic.tech
- Vertical Slice Architecture in .NET: complete guide · Milan Jovanović, 2026Secondarymilanjovanovic.tech
- Testing vertical slices in .NET · Milan Jovanović, 2026Secondarymilanjovanovic.tech
- Minimal Clean Architecture (min-clean) · Steve Smith (Ardalis) templateReferenceardalis.github.io
- SSW Rules: Vertical Slice Architecture · Consultancy guidanceSecondaryssw.com.au
- SSW.VerticalSliceArchitecture.Template · .NET template with architecture testsReferencenuget.org
- NetArchTestReferencegithub.com
Referenced by
- Which Architecture Style When? Comparison