@@ -22,23 +22,24 @@ description: >
2222
2323Every scaffolded feature MUST include ALL of the following. Do not skip any item:
2424
25+ - [ ] ** Result pattern** — Handlers return ` Result<T> ` , not raw responses. Endpoints map Result to HTTP (success → TypedResults, failure → ` ToProblemDetails() ` )
2526- [ ] ** CancellationToken** on every async method and passed to every async call
2627- [ ] ** FluentValidation** validator class with meaningful rules (ranges, required fields, max lengths)
2728- [ ] ** ValidationFilter wiring** — ` .AddEndpointFilter<ValidationFilter<T>>() ` on mutating endpoints
2829- [ ] ** OpenAPI metadata** — ` .WithName() ` , ` .WithSummary() ` , ` .Produces<T>() ` , ` .ProducesValidationProblem() ` , ` .ProducesProblem(404) `
2930- [ ] ** Pagination** on list endpoints — ` page ` , ` pageSize ` with bounded max (e.g., 50)
30- - [ ] ** Global error handler** — Verify ` app.UseExceptionHandler() ` + ` IExceptionHandler ` exists in Program.cs; scaffold it if missing
31- - [ ] ** appsettings.json** — Verify connection string exists; scaffold it with placeholder if missing
31+ - [ ] ** Global error handler** — Verify ` app.UseExceptionHandler() ` exists in Program.cs; scaffold if missing
32+ - [ ] ** appsettings.json** — Verify connection string exists; scaffold with placeholder if missing
3233- [ ] ** Integration test** with proper DI replacement using ` services.RemoveAll<DbContextOptions<T>>() `
3334
3435## Patterns
3536
3637### Feature Scaffold — Vertical Slice Architecture (VSA)
3738
38- Single-file feature with command, handler, validator , and response:
39+ Single-file feature with Result pattern, validation , and response:
3940
4041``` csharp
41- // Features/Orders/CreateOrder.cs
42+ // Features/Orders/CreateOrder.cs — handler returns Result<T>, not raw response
4243namespace MyApp .Features .Orders ;
4344
4445public static class CreateOrder
@@ -49,12 +50,12 @@ public static class CreateOrder
4950
5051 internal sealed class Handler (AppDbContext db , TimeProvider clock )
5152 {
52- public async Task <Response > HandleAsync (Command command , CancellationToken ct )
53+ public async Task <Result < Response > > HandleAsync (Command command , CancellationToken ct )
5354 {
5455 var order = Order .Create (command .CustomerId , command .Items , clock .GetUtcNow ());
5556 db .Orders .Add (order );
5657 await db .SaveChangesAsync (ct );
57- return new Response (order .Id , order .Total , order .CreatedAt );
58+ return Result . Success ( new Response (order .Id , order .Total , order .CreatedAt ) );
5859 }
5960 }
6061
@@ -74,7 +75,7 @@ public static class CreateOrder
7475}
7576```
7677
77- Endpoint group with full OpenAPI metadata, validation filter, and pagination:
78+ Endpoint group — maps Result to HTTP, full OpenAPI metadata, validation, pagination:
7879
7980``` csharp
8081// Features/Orders/OrderEndpoints.cs — auto-discovered via IEndpointGroup
@@ -85,28 +86,27 @@ public sealed class OrderEndpoints : IEndpointGroup
8586 var group = app .MapGroup (" /api/orders" ).WithTags (" Orders" );
8687
8788 group .MapPost (" /" , CreateOrderHandler )
88- .WithName (" CreateOrder" )
89- .WithSummary (" Create a new order" )
89+ .WithName (" CreateOrder" ).WithSummary (" Create a new order" )
9090 .Produces <CreateOrder .Response >(StatusCodes .Status201Created )
9191 .ProducesValidationProblem ()
9292 .AddEndpointFilter <ValidationFilter <CreateOrder .Command >>();
9393
9494 group .MapGet (" /" , ListOrdersHandler )
95- .WithName (" ListOrders" )
96- .WithSummary (" List orders with pagination" )
95+ .WithName (" ListOrders" ).WithSummary (" List orders with pagination" )
9796 .Produces <PagedList <OrderSummary >>();
9897
9998 group .MapGet (" /{id:guid}" , GetOrderHandler )
10099 .WithName (" GetOrder" )
101- .Produces <OrderDetail >()
102- .ProducesProblem (StatusCodes .Status404NotFound );
100+ .Produces <OrderDetail >().ProducesProblem (StatusCodes .Status404NotFound );
103101 }
104102
105- private static async Task <Created < CreateOrder . Response > > CreateOrderHandler (
103+ private static async Task <IResult > CreateOrderHandler (
106104 CreateOrder.Command cmd , CreateOrder.Handler handler , CancellationToken ct )
107105 {
108- var response = await handler .HandleAsync (cmd , ct );
109- return TypedResults .Created ($" /api/orders/{response .Id }" , response );
106+ var result = await handler .HandleAsync (cmd , ct );
107+ return result .IsSuccess
108+ ? TypedResults .Created ($" /api/orders/{result .Value .Id }" , result .Value )
109+ : result .ToProblemDetails ();
110110 }
111111
112112 private static async Task <Ok <PagedList <OrderSummary >>> ListOrdersHandler (
@@ -134,10 +134,8 @@ public record PaginationQuery(int Page = 1, int PageSize = 20)
134134 public int Page { get ; init ; } = Math .Max (1 , Page );
135135 public int PageSize { get ; init ; } = Math .Clamp (PageSize , 1 , 50 );
136136}
137-
138137public record PagedList <T >(List <T > Items , int TotalCount , int Page , int PageSize );
139138```
140- ```
141139
142140### Feature Scaffold — Clean Architecture (CA)
143141
0 commit comments