Sponsors
- Check out this powerful framework on ABP Framework for .NET. A game-changing platform for building robust, modular, and maintainable applications faster than ever!
Tired of projects with too many layers and files?
Low cohesion between layers, suddenly, making a small change requires navigating across multiple files and layers, just to understand what’s going on.
This is where Vertical Slice Architecture comes into play, is an extremely popular alternative to layered architectures, where you can organize your code by features instead of horizontal layers.
In this post, I’ll walk you through what Vertical Slice Architecture really means, why it’s worth considering in your projects, and how you can start using it today.
What Is Vertical Slice Architecture?
Vertical Slice is a design approach where the system is broken down by features, not layers.
It’s a slice through the entire application stack, each slice encapsulates everything needed to deliver a specific feature: request, validation, use case with mediatr, response, etc. All in one place. No jumping between layers or scattered logic.
Benefits:
- Clean and scalable structure: Easy to evolve as requirements grow, reducing the amount of conflicts by allowing developers to work on different features.
- Localized changes, better maintenance: Updates affect only the slice you’re working on, minimizing side effects and making it easier to maintain.
- Simplified structure: You stop thinking in layers and start thinking in actions.
- Better separation of concerns: Each feature owns its flow, dependencies, and rules, no accidental coupling with other parts of the app.
- Improved readability: A new developer can open a folder and instantly understand what the feature does.
That’s the idea behind vertical slices: keep it focused, keep it local, and make the next developer say “oh, that’s exactly what I needed to change.”
A simple way to picture vertical slices:

How to Implement Vertical Slice Architecture
In a vertical slice setup, the way I like to structure it the most is using a feature folder, each feature lives in its own folder or file.
Let’s say your system allows users to send and resend SMS messages.
With vertical slices, it might look like this:
public static class Send
{
public record Request(string From, string To, string Message, bool SendNow, DateTime Schedule);
public record Response(DateTime? ScheduledAt);
internal sealed class Validator : AbstractValidator<Request>
{
public Validator()
{
RuleFor(e => e.From)
.NotEmpty().WithMessage("Sender is required.")
.MaximumLength(11).WithMessage("Sender must be up to 11 characters.");
RuleFor(e => e.To)
.NotEmpty().WithMessage("Recipient is required.")
.MaximumLength(15).WithMessage("Recipient must be up to 15 digits.");
RuleFor(e => e.Message)
.NotEmpty().WithMessage("Message body is required.")
.MaximumLength(160).WithMessage("Message must be up to 160 characters.");
RuleFor(e => e.Schedule)
.NotEmpty().WithMessage("Schedule date is required.")
.Must(BeInTheFuture).When(e => !e.SendNow)
.WithMessage("Scheduled time must be in the future.");
}
private bool BeInTheFuture(DateTime schedule)
{
return schedule > DateTime.UtcNow;
}
}
public class SmsEndpoint : CarterModule
{
public SmsEndpoint() : base() { }
public override void AddRoutes(IEndpointRouteBuilder app)
{
app.MapPost("send", Handler);
}
public async static Task<IResult> Handler(
Request request,
IValidator<Request> validator)
{
var result = validator.Validate(request);
if (!result.IsValid)
{
var firstError = result.Errors.First();
var response = Result<bool>.Failure(Error.Validation("SEND.SMS.ERROR", firstError.ErrorMessage));
return response.ToHttpResult();
}
// ...
return Results.Ok(new Response(DateTime.UtcNow));
}
}
}
This structure also plays well with CQRS and tools like MediatR, but you don’t need either to apply it.
This maps naturally to a simple and effective structure known as REPR pattern, short for Request, Endpoint, and Response.
Here’s how it breaks down:
Request – Defines the input data needed to trigger the feature.
Endpoint – The minimal API route that exposes the feature and process.
Response – The result returned to the client (could be data, a status, or both).
REPR isn’t mandatory, but it brings structure and consistency to your vertical slices without forcing abstractions you don’t need.
At its core, each vertical slice follows a simple and predictable flow:

Here’s a practical structure for organizing related slices inside the /Features folder:
📁 Features
├── 📁 Sms
│ ├── 📁 Send
│ │ ├── SendRequest.cs
│ │ ├── SendHandler.cs
│ │ ├── SendValidator.cs
│ │ └── SendEndpoint.cs
│ └── 📁 Resend
│ ├── ResendRequest.cs
│ ├── ResendHandler.cs
│ ├── ResendValidator.cs
│ └── ResendEndpoint.cs
└── 📁 User
└── 📁 Register
├── RegisterRequest.cs
├── RegisterHandler.cs
├── RegisterValidator.cs
└── RegisterEndpoint.cs
There are several ways to structure a vertical slice, here is another approach using mediatr and CQRS:
📁 Features
└── 📁 Sms
├── 📁 SendSms
│ ├── SendSms.cs
│ ├── SendSms.Command.cs
│ ├── SendSms.Handler.cs
│ ├── SendSms.Validator.cs
│ └── SendSms.Endpoint.cs
└── 📁 GetStatusSms
├── GetStatusSms.cs
├── GetStatusSms.Query.cs
├── GetStatusSms.Handler.cs
├── GetStatusSms.Endpoint.cs
└── GetStatusSms.Response.cs
You can leave everything in just one file too:
📁 Features
└── 📁 Sms
├── SendSms.cs
└── ResendSms.cs
Why Should You Care?
Because most codebases don’t scale in clarity.
They start clean, with controllers calling services and services calling repositories. But over time, features get spread across multiple layers, files, and abstractions. Changing one thing means touching five. Testing one thing means mocking three.
Another important point: Vertical slices don’t need to replace Clean Architecture, you can structure your application around features (vertical slices), and still enforce boundaries like Domain using clean architecture concepts.
Enjoying the best of both worlds.
In a future post, I can show how you can combine Vertical Slices with Clean Architecture to keep features simple on the outside and maintain solid boundaries inside.
It’s the approach I use in real projects
Final Thoughts
Vertical Slice Architecture isn’t about hype or buzzwords, it’s about writing software that aligns with how you deliver features.
The result? Cleaner code, fewer dependencies, faster onboarding, and more predictable changes.
It scales with your team. It works in small services or large systems. And it forces you to think in terms of outcomes, not plumbing.
Try it in your next feature.
You don’t need a rewrite, just a different entry point.
Thank you for reading.
See you next time!
Sponsors
- Check out this powerful framework on ABP Framework for .NET. A game-changing platform for building robust, modular, and maintainable applications faster than ever!



