Lessons in this course (8)

4. Vertical Slice architecture

Intermediate★ Must knowLesson 4 of 8

Instead of horizontal layers, organise by feature/use case: everything needed for "Place order" lives together.

text
src/Shop.Api/
├── Features/
│   ├── Orders/
│   │   ├── PlaceOrder.cs          request + validator + handler + endpoint in one file (or a folder)
│   │   ├── GetOrderById.cs
│   │   ├── CancelOrder.cs
│   │   └── OrderMappings.cs
│   ├── Products/
│   │   ├── SearchProducts.cs      may use Dapper for a fast query
│   │   └── CreateProduct.cs       may use EF Core
│   └── Customers/ ...
├── Domain/                         shared entities (if any rich domain)
├── Infrastructure/                 DbContext, external clients
├── Common/                         behaviours, result types, exceptions
└── Program.cs                      maps all feature endpoints
csharp
public static class PlaceOrder
{
    public sealed record Command(int CustomerId, List<Line> Lines);
    public sealed record Line(int ProductId, int Qty);

    public sealed class Validator : AbstractValidator<Command>
    {
        public Validator() { RuleFor(c => c.Lines).NotEmpty(); RuleForEach(c => c.Lines).ChildRules(l => l.RuleFor(x => x.Qty).GreaterThan(0)); }
    }

    public static void MapEndpoint(IEndpointRouteBuilder app) =>
        app.MapPost("/api/orders", async (Command cmd, ShopDbContext db, IValidator<Command> v, CancellationToken ct) =>
        {
            var result = await v.ValidateAsync(cmd, ct);
            if (!result.IsValid) return Results.ValidationProblem(result.ToDictionary());
            var order = Order.Create(cmd.CustomerId, cmd.Lines.Select(l => (l.ProductId, l.Qty)));
            db.Orders.Add(order);
            await db.SaveChangesAsync(ct);
            return Results.Created($"/api/orders/{order.Id}", new { order.Id });
        });
}

Pros: a change touches one folder; features can use different techniques (EF here, Dapper there); easy to delete a feature; fits CQRS. Cons: possible duplication between slices; needs discipline to extract real shared domain logic.