Build or update the BlueBubbles external channel plugin for Moltbot (extension package, REST...
npx skills add akires47/agent-skills --skill "dotnet-vertical-slice"
Install specific skill from multi-skill repository
# Description
Create minimal APIs using vertical slice architecture. Use it when building APIs organized by feature rather than technical layers, with result pattern for error handling, and fluent validation pattern. No external packages required.
# SKILL.md
name: dotnet-vertical-slice
description: Create minimal APIs using vertical slice architecture. Use it when building APIs organized by feature rather than technical layers, with result pattern for error handling, and fluent validation pattern. No external packages required.
.NET 10 Vertical Slice Architecture
Organize code by feature, not by layer. Each feature is self-contained with its endpoint, request/response, validation, and handler in a single file.
Project Structure
src/
├── Features/
│ ├── Products/
│ │ ├── GetProduct.cs
│ │ ├── CreateProduct.cs
│ │ └── UpdateProduct.cs
│ └── Orders/
│ └── ...
├── Shared/
│ ├── Results/
│ │ ├── Result.cs
│ │ └── Error.cs
│ └── Validation/
│ └── ValidationResult.cs
├── Entities/
└── Program.cs
Feature Slice Pattern
One file per operation containing everything needed:
// Features/Products/CreateProduct.cs
public static class CreateProduct
{
public sealed record Request(string Name, decimal Price);
public sealed record Response(int Id, string Name, decimal Price);
public static async Task<Result<Response>> HandleAsync(
Request request, AppDbContext db, CancellationToken ct)
{
var validation = Validate(request);
if (!validation.IsValid)
return validation.ToResult<Response>(null!);
var product = ToEntity(request);
db.Products.Add(product);
await db.SaveChangesAsync(ct);
return ToResponse(product);
}
private static ValidationResult Validate(Request request) =>
ValidationExtensions.Validate()
.NotEmpty(request.Name, "Name")
.GreaterThan(request.Price, 0, "Price");
private static Product ToEntity(Request request) =>
new() { Name = request.Name, Price = request.Price };
private static Response ToResponse(Product product) =>
new(product.Id, product.Name, product.Price);
public static void MapEndpoint(IEndpointRouteBuilder app) => app
.MapPost("/api/products", async (Request request, AppDbContext db, CancellationToken ct) =>
(await HandleAsync(request, db, ct)).ToCreatedResponse(r => $"/api/products/{r.Id}"))
.WithName("CreateProduct")
.WithTags("Products");
}
Core Principles
- Result pattern only - Never throw exceptions, return
Result<T>orResult - Static handlers - Use
public static async Task<Result<T>> HandleAsync(...) - Inline validation - Validate at handler start, return early on failure
- Inline mapping - Use private
ToEntity()orToResponse()methods within each feature - Projections - Use
.Select()for queries, avoid loading full entities
References
See detailed implementations in the references/ folder:
- Result Pattern - Error types, Result class, HTTP mapping
- Validation - ValidationResult, fluent extensions
- Feature Examples - CRUD, filtering, pagination
Guidelines
- One feature = one file (endpoint + request/response + validation + handler)
- Name files by operation:
CreateProduct.cs,GetProducts.cs - Keep entities in shared folder (only cross-cutting concern)
- Use
[AsParameters]for query parameters - Group endpoints with
.WithTags()for OpenAPI
# Supported AI Coding Agents
This skill is compatible with the SKILL.md standard and works with all major AI coding agents:
Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.