Vertical Slice

codewithmukesh/dotnet-claude-kit/skills/vertical-slice

作者 codewithmukesh23300897f4d1无许可证754 个星标收录于 2026年10月8日更新于 2026年10月8日仓库2个月前更新

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.

AI 生成的概览

指导 .NET 开发者采用垂直切片架构组织应用,涵盖功能文件夹、端点分组与处理程序模式。

功能
该技能介绍 .NET 应用的垂直切片架构(VSA),说明功能文件夹、端点分组和处理程序模式。它给出三种处理程序方案——源生成 Mediator、Wolverine 约定以及原生处理程序类——并附有各自的代码示例。内容还涉及可选的模块边界、共享横切关注点、反模式以及选择模式的决策指南。
适用场景
当架构顾问推荐 VSA、在现有 VSA 代码库中工作,或向功能文件夹项目添加功能时使用。讨论垂直切片模式、功能文件夹或处理程序模式时也适用。
运行要求
不附带脚本,仅为说明性内容。示例假定存在 .NET 项目,并引用 Mediator.Abstractions、Mediator.SourceGenerator、Wolverine、FluentValidation 和 Entity Framework Core 等包,但阅读这些指导无需安装任何内容。

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.

csharp
// 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 DIbuilder.Services.AddMediator();
// Features/Orders/OrderEndpoints.cs — auto-discovered via IEndpointGrouppublic 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.

csharp
// 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.

csharp
// 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 responsegroup.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.Contracts project with DTOs/interfaces (use sparingly)

Shared Concerns

Cross-cutting concerns live outside feature folders:

csharp
// 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

csharp
// BAD — a feature folder with its own service layer and repositoryFeatures/  Orders/    CreateOrder.cs    IOrderService.cs         # unnecessary abstraction    OrderService.cs          # unnecessary abstraction    IOrderRepository.cs      # unnecessary abstraction    OrderRepository.cs       # unnecessary abstraction
// GOOD — handler talks directly to DbContextFeatures/  Orders/    CreateOrder.cs           # handler uses AppDbContext directly

Don't Cross-reference Features Directly

csharp
// BAD — CreateOrder directly calls GetProduct handlervar product = await _getProductHandler.Handle(new GetProduct.Query(productId));
// GOOD — query the database directly or use a shared read modelvar product = await db.Products.FindAsync(productId, ct);

Don't Put Everything in One God Feature File

csharp
// BAD — 500-line file with CRUD + business logic + mappingpublic static class Orders{    // Create, Read, Update, Delete, Cancel, Refund, Export...}
// GOOD — one file per operationFeatures/Orders/CreateOrder.csFeatures/Orders/GetOrder.csFeatures/Orders/CancelOrder.cs

Decision Guide

ScenarioRecommendation
New project (default)Pattern A — Mediator (source-generated, MIT, fast)
Need mediator + messaging in one libPattern B — Wolverine (also handles events/queues)
Want full control, no dependenciesPattern C — Raw handler classes
Existing MediatR codebase with licenseKeep MediatR if licensed; otherwise migrate to Mediator (near-identical API)
Monolith growing complexAdd module boundaries, keep VSA within each module
Simple CRUD featureSingle file: request + handler + endpoint
Complex feature (saga, events)Multiple files in feature folder, still colocated
Sharing logic between featuresExtract to Common/ — not to another feature

来源与署名

来源:codewithmukesh/dotnet-claude-kit位于skills/vertical-slice提交2330089

许可证: 无许可证

内容归原作者所有。SourceWeft 从公开仓库中收录这些内容。

举报或申请下架

更多来自 codewithmukesh/dotnet-claude-kit 的技能

Wrap Up

codewithmukesh

在会话结束时把已完成工作、待办任务与经验写入交接文件,并在会话开始时重新载入。

Productivity & Workflow7542个月前更新

Workflow Mastery

codewithmukesh

Claude Code workflow mastery for .NET developers. Covers parallel execution with git worktrees, plan mode strategy, verification loops, auto-formatting hooks, permission setup for dotnet CLI, prompting techniques, subagent patterns, and context discipline — token budget management, MCP-first navigation, lazy loading, and subagent isolation — all adapted for the .NET ecosystem. Load this skill when setting up Claude Code for a .NET project, optimizing workflows, running parallel sessions, when context is running low or sessions feel sluggish, when exploring a large codebase efficiently, or when the user mentions "productivity", "workflow", "parallel", "worktree", "plan mode", "permissions", "hooks", "10x", "setup Claude Code", "speed up development", "context", "tokens", "budget", "running out of context", "too many files", or "large codebase". Inspired by tips from Boris Cherny (creator of Claude Code) and the Anthropic team.

待分类7542个月前更新

Testing

codewithmukesh

Testing strategy for .NET 10 applications. Covers xUnit v3, WebApplicationFactory for integration tests, Testcontainers for real database testing, Verify for snapshot testing, and the AAA pattern. Load this skill when writing tests, setting up test infrastructure, reviewing test coverage, or when the user mentions "test", "xUnit", "WebApplicationFactory", "Testcontainers", "integration test", "unit test", "bUnit", "snapshot test", "Verify", "test coverage", "AAA pattern", "WireMock", or "FakeTimeProvider".

待分类7542个月前更新

Tdd

codewithmukesh

Guided test-driven development workflow for .NET 10 using xUnit v3, WebApplicationFactory, Testcontainers, and Verify snapshots. Follows the strict red-green-refactor cycle. Use when: "TDD", "test-driven", "let's TDD this", "red green refactor", "write the test first", or when building a feature with clear acceptance criteria.

待分类7542个月前更新

Spec

codewithmukesh

通过结构化提问,把模糊的功能想法转化为双方确认并持久化的规格说明文件。

Productivity & Workflow7542个月前更新

Serilog

codewithmukesh

Structured logging with Serilog for .NET 10 applications. Covers two-stage bootstrap, appsettings configuration, enrichers, sinks, request logging, destructuring, and Serilog.Expressions. Load this skill when setting up Serilog, configuring log sinks, enrichers, or structured logging, or when the user mentions "Serilog", "structured logging", "log enrichment", "Seq", "LogContext", "UseSerilog", "WriteTo", "message template", "Serilog.Expressions", "request logging", "log sink", "rolling file", or "audit log".

待分类7542个月前更新