vertical-slice
codewithmukesh/dotnet-claude-kit
Vertical Slice Architecture for .NET — organize features as self-contained slices, not layers.
What is vertical-slice?
Vertical Slice Architecture (VSA) organizes .NET applications by feature rather than technical layer, with each feature containing its endpoint, handler, request/response types, and validation. Use this skill when adopting VSA, working in an existing VSA codebase, adding features to a feature-folder project, or discussing vertical slice patterns and handler architectures.
- Organize code by feature in self-contained vertical slices instead of Controllers/Services/Repositories layers
- Support three handler patterns: Mediator (source-generated, AOT-compatible), Wolverine (convention-based), or raw handler classes
- Minimize cross-feature coupling by keeping shared concerns in Common/ or Shared/ directories
- Structure feature folders with one file per feature, extracting only when complexity demands it
- Enable module boundaries for larger applications with separate class libraries, DbContexts, and integration events
- Provide pipeline behaviors and validation patterns for cross-cutting concerns
How to install vertical-slice
npx skills add https://github.com/codewithmukesh/dotnet-claude-kit --skill vertical-slice- A .NET project (ASP.NET Core recommended)
- One of: Mediator.Abstractions + Mediator.SourceGenerator, Wolverine, or no external library (raw handlers)
- FluentValidation (optional, for validation patterns)
- Entity Framework Core (optional, for persistence examples)
How to use vertical-slice
- 1.Create a Features/ folder at the root of your API project
- 2.Within Features/, create a subfolder for each domain (Orders, Products, etc.)
- 3.In each domain folder, create one .cs file per operation (CreateOrder.cs, GetOrder.cs, etc.) containing the Command/Query, Handler, and Response record
- 4.Choose a handler pattern: Mediator (recommended), Wolverine, or raw classes
- 5.Register handlers in Program.cs (AddMediator(), AddWolverine(), or manual DI)
- 6.Create an IEndpointGroup or endpoint extension to map HTTP routes to handlers
- 7.Place shared logic (mappers, validators, behaviors) in Common/ or domain-specific Shared/ folders
- 8.For larger apps, introduce module boundaries by separating features into class libraries with their own DbContext and module registration
Use cases
- Building a new .NET API with clear feature boundaries and minimal layer-jumping
- Migrating an existing layered architecture to vertical slices for faster feature development
- Adding new features to an existing VSA codebase while maintaining folder structure consistency
- Designing module boundaries for a growing application that needs separate Orders, Catalog, and other domain modules
- Implementing validation and cross-cutting concerns via Mediator pipeline behaviors or raw handlers
- Backend developers building .NET APIs with feature-driven architecture
- Teams adopting vertical slice architecture for the first time
- Architects designing modular .NET applications with clear feature boundaries
- Developers using dotnet-claude-kit for architecture guidance and code generation
vertical-slice FAQ
Yes, start with one file per feature containing the request, handler, response, and validator. Extract to separate files only when the file grows too large or complexity demands it. This keeps related code together and reduces file-jumping.
Features should not reference each other directly. Use integration events (via Wolverine or MassTransit) for async, decoupled communication, or share contracts via a MyApp.Contracts project sparingly. This minimizes coupling and makes features independently testable.
Mediator is source-generated, MIT-licensed, and AOT-compatible with a familiar MediatR-like API. Wolverine is convention-based and discovers handlers by method signature. Raw handlers have no external dependency and give full control. Choose based on your team's preference and project size.
Introduce module boundaries when your application grows beyond a single project and you need separate DbContexts, feature sets, or deployment units. Each module is a class library with its own Features/, Persistence/, and module registration, communicating via integration events.
Use FluentValidation validators in the same file as your handler, then apply them via a ValidationBehavior (Mediator) or ValidationFilter (endpoints). This keeps validation logic close to the request it validates.
Full instructions (SKILL.md)
Source of truth, from codewithmukesh/dotnet-claude-kit.
name: vertical-slice description: > Vertical Slice Architecture (VSA) for .NET applications — one of several supported architectures in dotnet-claude-kit. Covers feature folders, endpoint grouping, and handler patterns for Mediator, Wolverine, and raw handler classes. Load this skill when the architecture-advisor recommends VSA, when working in an existing VSA codebase, when adding features to a feature-folder project, or when discussing vertical slice patterns, feature folders, or handler patterns.
Vertical Slice Architecture (VSA)
Core Principles
- Organize by feature, not by layer — Each feature is a self-contained vertical slice containing its endpoint, handler, request/response types, and validation. No more jumping between Controllers/, Services/, Repositories/ folders.
- Minimize cross-feature coupling — Features should not reference each other directly. Shared concerns go in a
Common/orShared/directory. - One file per feature is fine — A simple CRUD endpoint doesn't need 5 files spread across layers. Start with everything in one file, extract only when complexity demands it.
- The handler is the unit of work — Each handler does one thing. No god-services with 20 methods.
Patterns
Feature Folder Structure
src/
MyApp.Api/
Features/
Orders/
CreateOrder.cs # Request, Handler, Response, Endpoint — all in one file
GetOrder.cs
ListOrders.cs
CancelOrder.cs
Shared/
OrderMapper.cs # Shared within the Orders feature only
Products/
CreateProduct.cs
GetProduct.cs
Common/
Behaviors/
ValidationBehavior.cs # Cross-cutting Mediator pipeline behavior
Persistence/
AppDbContext.cs
Extensions/
ServiceCollectionExtensions.cs
Program.cs
Pattern A: Mediator Handlers (Recommended Default)
Source-generated mediator — MIT licensed, no reflection, Native AOT compatible. Uses IRequest<T> / IRequestHandler<TRequest, TResponse> with pipeline behaviors. Near-identical API to MediatR but faster and free. Package: Mediator.Abstractions + Mediator.SourceGenerator.
// Features/Orders/CreateOrder.cs
public static class CreateOrder
{
public record Command(string CustomerId, List<OrderItemDto> Items) : IRequest<Result<OrderResponse>>;
public record OrderItemDto(string ProductId, int Quantity);
public record OrderResponse(Guid Id, decimal Total, DateTime CreatedAt);
public class Validator : AbstractValidator<Command>
{
public Validator()
{
RuleFor(x => x.CustomerId).NotEmpty();
RuleFor(x => x.Items).NotEmpty();
RuleForEach(x => x.Items).ChildRules(item =>
{
item.RuleFor(x => x.ProductId).NotEmpty();
item.RuleFor(x => x.Quantity).GreaterThan(0);
});
}
}
internal sealed class Handler(AppDbContext db, TimeProvider clock) : IRequestHandler<Command, Result<OrderResponse>>
{
public async ValueTask<Result<OrderResponse>> Handle(Command request, CancellationToken ct)
{
var order = Order.Create(request.CustomerId, request.Items, clock.GetUtcNow());
db.Orders.Add(order);
await db.SaveChangesAsync(ct);
return Result.Success(new OrderResponse(order.Id, order.Total, order.CreatedAt));
}
}
}
// Registration in Program.cs or module DI
builder.Services.AddMediator();
// Features/Orders/OrderEndpoints.cs — auto-discovered via IEndpointGroup
public sealed class OrderEndpoints : IEndpointGroup
{
public void Map(IEndpointRouteBuilder app)
{
var group = app.MapGroup("/api/orders").WithTags("Orders");
group.MapPost("/", async (CreateOrder.Command command, ISender sender, CancellationToken ct) =>
{
var result = await sender.Send(command, ct);
return result.IsSuccess
? TypedResults.Created($"/api/orders/{result.Value.Id}", result.Value)
: result.ToProblemDetails();
})
.WithName("CreateOrder").Produces<CreateOrder.OrderResponse>(201)
.ProducesValidationProblem()
.AddEndpointFilter<ValidationFilter<CreateOrder.Command>>();
}
}
Pattern B: Wolverine Handlers
Convention-based — no interfaces to implement. Wolverine discovers handlers by method signature.
// Features/Orders/CreateOrder.cs
public static class CreateOrder
{
public record Command(string CustomerId, List<OrderItemDto> Items);
public record OrderItemDto(string ProductId, int Quantity);
public record OrderResponse(Guid Id, decimal Total, DateTime CreatedAt);
// Wolverine discovers this by convention (static Handle method)
public static async Task<Result<OrderResponse>> Handle(
Command command,
AppDbContext db,
TimeProvider clock,
CancellationToken ct)
{
var order = Order.Create(command.CustomerId, command.Items, clock.GetUtcNow());
db.Orders.Add(order);
await db.SaveChangesAsync(ct);
return Result.Success(new OrderResponse(order.Id, order.Total, order.CreatedAt));
}
}
Pattern C: Raw Handler Classes (No Library)
Direct handler classes with no external dependency. Good for small projects or teams that want full control.
// Features/Orders/CreateOrder.cs
public static class CreateOrder
{
public record Command(string CustomerId, List<OrderItemDto> Items);
public record OrderItemDto(string ProductId, int Quantity);
public record OrderResponse(Guid Id, decimal Total, DateTime CreatedAt);
internal class Handler(AppDbContext db, TimeProvider clock)
{
public async Task<Result<OrderResponse>> ExecuteAsync(Command command, CancellationToken ct)
{
var order = Order.Create(command.CustomerId, command.Items, clock.GetUtcNow());
db.Orders.Add(order);
await db.SaveChangesAsync(ct);
return Result.Success(new OrderResponse(order.Id, order.Total, order.CreatedAt));
}
}
}
// Endpoint wiring — Result maps to HTTP response
group.MapPost("/", async (CreateOrder.Command command, CreateOrder.Handler handler, CancellationToken ct) =>
{
var result = await handler.ExecuteAsync(command, ct);
return result.IsSuccess
? TypedResults.Created($"/api/orders/{result.Value.Id}", result.Value)
: result.ToProblemDetails();
});
Adding Module Boundaries (Optional)
For larger applications that grow beyond a single project, introduce module boundaries. Each module is a separate class library with its own features and DbContext.
src/
MyApp.Api/ # Host — wires modules together
Program.cs
Modules/
ModuleExtensions.cs # app.MapOrderModule(), app.MapCatalogModule()
MyApp.Orders/ # Module — own features, own DbContext
Features/
CreateOrder.cs
Persistence/
OrdersDbContext.cs
OrdersModule.cs # IServiceCollection + IEndpointRouteBuilder extensions
MyApp.Catalog/ # Module
Features/
CreateProduct.cs
Persistence/
CatalogDbContext.cs
CatalogModule.cs
Modules communicate via:
- Integration events (preferred) — async, decoupled via Wolverine or MassTransit
- Shared contracts — a
MyApp.Contractsproject with DTOs/interfaces (use sparingly)
Shared Concerns
Cross-cutting concerns live outside feature folders:
// Common/Behaviors/ValidationBehavior.cs (Mediator pipeline)
public sealed class ValidationBehavior<TRequest, TResponse>(IEnumerable<IValidator<TRequest>> validators)
: IPipelineBehavior<TRequest, TResponse>
where TRequest : IMessage
{
public async ValueTask<TResponse> Handle(
TRequest request,
MessageHandlerDelegate<TRequest, TResponse> next,
CancellationToken ct)
{
var context = new ValidationContext<TRequest>(request);
var failures = validators
.Select(v => v.Validate(context))
.SelectMany(r => r.Errors)
.Where(f => f is not null)
.ToList();
if (failures.Count > 0)
throw new ValidationException(failures);
return await next(request, ct);
}
}
Anti-patterns
Don't Create Layered Abstractions Within a Slice
// BAD — a feature folder with its own service layer and repository
Features/
Orders/
CreateOrder.cs
IOrderService.cs # unnecessary abstraction
OrderService.cs # unnecessary abstraction
IOrderRepository.cs # unnecessary abstraction
OrderRepository.cs # unnecessary abstraction
// GOOD — handler talks directly to DbContext
Features/
Orders/
CreateOrder.cs # handler uses AppDbContext directly
Don't Cross-reference Features Directly
// BAD — CreateOrder directly calls GetProduct handler
var product = await _getProductHandler.Handle(new GetProduct.Query(productId));
// GOOD — query the database directly or use a shared read model
var product = await db.Products.FindAsync(productId, ct);
Don't Put Everything in One God Feature File
// BAD — 500-line file with CRUD + business logic + mapping
public static class Orders
{
// Create, Read, Update, Delete, Cancel, Refund, Export...
}
// GOOD — one file per operation
Features/Orders/CreateOrder.cs
Features/Orders/GetOrder.cs
Features/Orders/CancelOrder.cs
Decision Guide
| Scenario | Recommendation |
|---|---|
| New project (default) | Pattern A — Mediator (source-generated, MIT, fast) |
| Need mediator + messaging in one lib | Pattern B — Wolverine (also handles events/queues) |
| Want full control, no dependencies | Pattern C — Raw handler classes |
| Existing MediatR codebase with license | Keep MediatR if licensed; otherwise migrate to Mediator (near-identical API) |
| Monolith growing complex | Add module boundaries, keep VSA within each module |
| Simple CRUD feature | Single file: request + handler + endpoint |
| Complex feature (saga, events) | Multiple files in feature folder, still colocated |
| Sharing logic between features | Extract to Common/ — not to another feature |
Related skills
More from codewithmukesh/dotnet-claude-kit and the wider catalog.

workflow-mastery
Master Claude Code workflows for .NET: parallel sessions, plan mode, verification loops, and context optimization.

wrap-up
Capture session work, pending tasks, and learnings into a handoff file for continuity across sessions.

api-versioning
API versioning strategies for ASP.NET Core using Asp.Versioning library with URL, header, and query string approaches.

arch-check
Verify your codebase matches its declared architecture—catch dependency violations, layer leaks, and cycles.

mintlify
Comprehensive reference for building and configuring Mintlify documentation sites.

codex-theme-installer
Download and install published Codex themes from codexthemes.ai into your local theme library.