Skip to content
Development
Skill

/vertical-slice

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

BOOST
From plugin
dotnet-claude-kit
76147 skills10 agents2 hooks1 MCP
Install
$ npx -y skills add codewithmukesh/dotnet-claude-kit --skill vertical-slice --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/vertical-slice

Context preview

The summary Claude sees to decide when to auto-load this skill.

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

SKILL.md

vertical-slice.SKILL.md
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

1. **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. 2. **Minimize cross-feature coupling** — Features should not reference each other directly. Shared concerns go in a `Common/` or `Shared/` directory. 3. **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. 4. **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.GetUt
Read more
Ships withdotnet-claude-kit

Make Claude Code a .NET 10 Expert

Get the whole plugin

Other skills on dotnet-claude-kit.