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

在 session 結束時把已完成工作、待辦事項與經驗寫入交接檔案,並在 session 開始時重新載入。

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 個月前更新