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

A software feature flowing from request to behaviour, data, and tests in one vertical slice

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:

  1. A simple request repeatedly requires changes across many layers.
  2. Small changes in shared services break unrelated feature tests.
  3. Mock-heavy tests protect call ordering rather than behaviour.
  4. The team struggles to find where one business rule lives.
  5. 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

  1. Does this code change because of the same user intent?
  2. Can a feature's request, validation, handler, response, and tests be found together?
  3. Can I meet the need without accessing another feature's internal class?
  4. Does this shared abstraction change for the same reason in at least three independent places?
  5. Are aggregate rules separate from application orchestration?
  6. Is the feature boundary protected by architectural tests in a modular monolith?
  7. 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

  1. Layered architecture is not bad; coordination cost grows when the unit of change is spread across layers.
  2. Vertical Slice groups code around user intent and behaviour rather than technical roles.
  3. Strategic duplication can cost less than a wrong abstraction.
  4. 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

Related articles

Related articles

Paylaş