Playbook
From Layers to Features: Why Did Vertical Slice Emerge? (From Layers To Features Why Vertical Slice Emerged)
Why does layered architecture slow change as a system grows? An architectural guide to Vertical Slice as a decision about feature ownership, behaviour…
Vertical Slice — Feature-First Engineering
Part 1 of 4
Do not blame CRUD or layers first
Layered architecture is a sound starting point for many small and mid-sized systems. Separating controllers, application services, repositories, and data access gives a team a shared technical language. The problem is not that layers exist. It begins when one user request is scattered across every layer and every change becomes coordination work.
A small request arrives: add a delivery note to an order.
Controller
→ Request / DTO
→ Service
→ Validator
→ Repository
→ Mapping
→ Test doubles
→ Tests
Changing eight files is not inherently wrong. But if they all change for the same behaviour, at the same time, by the same team, the filesystem is describing technical roles rather than business value. Vertical Slice is a pragmatic response to that friction.
Concepts at first use
📦 Vertical Slice
An end-to-end feature unit that keeps one user intent from request through persistence and tests within one boundary.
📦 Locality of behaviour
Keeping the code required to understand or change a behaviour physically close together.
📦 Temporal DRY
A DRY lens that asks whether similar code will change for the same reason over time.
📦 Wrong abstraction
An abstraction shared only because repetition exists, which later binds different needs together.
Vertical Slice does not ban layers. It keeps technical separation inside the feature while user intent, business rules, and ownership determine the outside boundary.
The story: when layers start to slow delivery
On day one, CreateOrder is small. One controller, one service, and one repository seem sufficient. Then inventory checks, campaign rules, delivery preferences, and audit records arrive. When every feature enters the same OrderService, the riskiest file becomes the most commonly shared file.
New feature
→ touches shared Service
→ affects unrelated tests
→ waits for another team's release
CRUD has not failed. The responsibility of the model and application layer has changed. Vertical Slice asks: which business intent causes this behaviour to change?
Horizontal versus vertical organisation
| Criterion | Layer-oriented | Feature-oriented |
|---|---|---|
| Organising axis | Technical role | User intent / feature |
| Unit of change | Files across multiple layers | One feature boundary |
| Code sharing | Tendency to share services early | Proven commonality |
| Navigation | Controller → service → repository | Feature → request → handler → test |
| Failure effect | Can spread through shared layers | Stays more visible at the feature boundary |
❌ Controllers / Services / Repositories
→ OrdersController
→ OrderService
→ OrderRepository
✓ Features / Orders / CreateOrder
→ Request
→ Validator
→ Handler
→ Response
→ Tests
This is not merely a naming choice. It changes the cost to find, understand, test, and reverse a change. Even if search through n files remains O(n) in the worst case, colocating relevant code reduces irrelevant navigation and mental context switches.
Screaming Architecture: what should a directory say?
If opening a project reveals Controllers, Services, and Infrastructure, it tells you about tools. If it reveals Orders, Catalog, Billing, and Returns, it tells you about the business. Architecture should make product intent scream louder than the framework.
A developer changing CancelOrder should find its handler, validation, response, and tests together. A feature directory is not a miniature monolith with everything public, though. Its entry point remains explicit; its implementation details stay hidden.
Cohesion, encapsulation, and strategic duplication
One slice should not know another slice's internal implementation. Share behaviour only when it is genuinely stable and changes for the same reason across several independent needs. Otherwise, a Shared folder becomes a new layer whose boundaries are invisible.
CreateOrder
→ asks the Order aggregate to act
→ completes a local transaction
→ returns a response
CancelOrder
→ applies its own rules
→ does not call CreateOrder's handler
Duplication can be the right cost. If two similar pieces of code will change for different business decisions, merging them early can spread each future change to O(k) dependent features.
Migration signals
Moving to Vertical Slice just to look modern is not enough. The investment returns when these signals appear together:
- A simple request repeatedly requires changes across many layers.
- Small changes in shared services break unrelated feature tests.
- Mock-heavy tests protect call ordering rather than behaviour.
- The team struggles to find where one business rule lives.
- Feature owners and delivery rhythms have diverged.
Do not leave these boundaries to documentation. In .NET, architectural tests can prevent one feature from depending on another feature's internal details. The rule costs O(dependency) scanning during build or test; its benefit is exposing a boundary violation before production.
Relationship to DDD and CQRS
Vertical Slice organises the application. DDD explains where business language and rules live. CQRS can separate the command and query flow of a use case. They are not competitors; they are different layers of architectural decision.
Feature boundary → Vertical Slice
Consistency rule → DDD Aggregate
Read / write flow → CQRS
Deployment unit → Modular monolith or microservice
Proving a feature boundary inside a modular monolith tests the independence claim without paying microservice costs too early.
Associations that create false confidence
❌ Vertical Slice = renamed folders
✓ It keeps user intent, behaviour, and ownership in one boundary.
❌ DRY = share every similar line
✓ Share code that changes for the same reason.
❌ Handler = every business rule
✓ A handler orchestrates; domain rules live in the appropriate model.
❌ Vertical Slice = microservice
✓ It is first an application boundary that can be proven in a modular monolith.
❌ Shared = free reuse
✓ Shared code also adds versioning and coordination cost.
Decision checklist
- Does this code change because of the same user intent?
- Can a feature's request, validation, handler, response, and tests be found together?
- Can I meet the need without accessing another feature's internal class?
- Does this shared abstraction change for the same reason in at least three independent places?
- Are aggregate rules separate from application orchestration?
- Is the feature boundary protected by architectural tests in a modular monolith?
- Are failure impact, test coverage, and rollback visible after a change?
The goal is not more folders. It is a smaller surface of code and decisions required to change one behaviour.
What should remain with you
- Layered architecture is not bad; coordination cost grows when the unit of change is spread across layers.
- Vertical Slice groups code around user intent and behaviour rather than technical roles.
- Strategic duplication can cost less than a wrong abstraction.
- Vertical Slice is an application-organisation decision that works with DDD, CQRS, and a modular monolith.
The goal of Vertical Slice is not to stack files vertically. It is to contain the effect of change inside the right feature boundary.
Next, we will examine how a slice works from the inside: request, validation, handler, mapping, and tests.
FAQ
Frequently asked questions
What is Vertical Slice?
An end-to-end feature unit that keeps one user intent from request through persistence and tests within one boundary.
What is Locality of behaviour?
Keeping the code required to understand or change a behaviour physically close together.
Is it true that "Vertical Slice = renamed folders"?
It keeps user intent, behaviour, and ownership in one boundary.
What does this part lock in?
Changing eight files is not inherently wrong. But if they all change for the same behaviour, at the same time, by the same team, the filesystem is describing technical roles rather than business value. Vertical Slice is a pragmatic response to that friction. Layered architecture is not bad; coordination cost grows when the unit of change is spread across layers. Layered architecture is a sound starting point for many small and mid-sized systems. Separating controllers, application services, repositories, and data access gives a team a shared technical language. The problem is not that layers exist. It begins when one user request is scattered across every layer and every change becomes coordination work.
Engineering Principles Learned
- Code organisation should make the reason for change visible before technical layers.
- Locality of behaviour is an architectural decision that reduces navigation and test cost.
- Sharing is valuable only when the reason for change is shared; otherwise duplication can be safer.
Continue reading
Continue reading
Next in series
How Does a Vertical Slice Work from the Inside?
A technical guide to the inner flow of a Vertical Slice: request, validation, handler, aggregate, outbox, projection, idempotency, performance, tests, and…
Related articles
From CRUD (Create, Read, Update, Delete) to CQRS (Command Query Responsibility Segregation): The Problem Is Not Code, It Is the Model
What is CQRS, what is the difference between CRUD and CQRS, and when should CQRS be used? A guide to why one model stops being enough in larger systems.
Related articles
DDD- Designing Software Around the Business, Not the Database
What is Domain-Driven Design? A guide to the limits of data-driven design, the power of ubiquitous language, and when DDD is a worthwhile investment.