VSA.NET 10August 2026 · 6 min read

Vertical Slice Architecture in Practice: A Walkthrough of 10 Real Slices

TL;DR

In practice, a vertical slice is an end-to-end behavior: the query or command, the handler, the validation, and the data access all live together. This walkthrough of ten real slices from production .NET 10 code shows how the same pattern scales from a 40-line CRUD endpoint to multi-step workflows with domain events.

Vertical Slice Architecture looks elegant in diagrams, but the real test is what it feels like to build ten features in a row. This walkthrough follows the structure used across the Indotalent product family — ten real slices taken from production .NET 10 code, covering everything from a one-file CRUD endpoint to a multi-step workflow that publishes domain events. By the end, you will know exactly what a slice is, what belongs inside it, and what belongs outside.

The Shape of a Vertical Slice Architecture Codebase

Before looking at individual slices, it helps to see the shape of the whole tree. Every Indotalent module follows the same skeleton: a Features folder, a shared Domain folder for the entity model, a Common folder for infrastructure, and no Controllers or Services folders anywhere.

/Features
  /Customers
  /Orders
  /Products
  /Invoices
  /Warehouse
  /Payments
/Shared
  /Domain
  /Common
  /Db

The Shared folders are the exception to the "everything lives in the slice" rule, and they are kept deliberately thin: only the entity model, the DbContext, and generic infrastructure live there. Everything else — validation rules, policies, projections, workflows — lives in a feature folder.

Feature routes are registered the same way everything else is — with a small extension that walks the feature folders and maps their endpoints once. The registration happens in Program.cs, and it is the only place where slices meet the composition root. Adding a feature never changes the registration code, because the route-group pattern is uniform across slices.

Slices 1–3: The CRUD Trio

The most common slice in any business application is simple CRUD, and three slices show the pattern at its smallest and most honest. These are the workhorses of the system, and VSA keeps them boringly simple:

  • GetCustomerList — a query that returns a paged result set using a projection to a DTO. The whole slice is a query, a handler, and an endpoint.
  • GetCustomerById — a query that returns one record or a 404. Nothing more.
  • CreateCustomer — a command that validates the request, checks the uniqueness of the email, writes the row, and returns the created record.

Each of these is 30 to 60 lines of code. The detail worth noticing is how little they share: the query handlers read directly from the DbContext, while the command handler takes a dependency on a small domain service for the uniqueness check. They are three files in one folder, not three layers.

Slices 4–5: Aggregates and Domain Events

Not every slice is a thin CRUD wrapper. Slice four, PlaceOrder, coordinates an aggregate: it loads the customer, validates credit, applies discounts, reduces stock, and publishes an OrderPlaced domain event. Slice five, CancelOrder, is its mirror image — it restores stock, reverses the payment intent, and publishes OrderCancelled. The two slices share the Order aggregate but remain completely independent files, so the cancellation flow can be tested and changed without touching order placement.

The domain service in these slices is worth a closer look: it is a concrete class, not an interface backed by a repository. When the credit-check rule becomes a stored procedure or a cached lookup, you change one class and both write-side slices pick it up automatically — no mapping, no adapter, no ripple.

Slices 6–8: Stateful Workflows

Slices six through eight show how VSA handles stateful workflows. CreateShipment builds a warehouse document from a set of order lines. ConfirmShipment advances the state machine and emits an event that triggers invoicing in a different feature folder. ReturnProduct starts a returns workflow that can suspend a customer account when fraud rules fire. The state machine transitions live inside each slice, so reading one folder tells you the entire story of that behavior — including the states it can and cannot enter.

Slices 9–10: Read Models and Reporting

The last two slices are read models. GetSalesDashboard aggregates thousands of rows into a KPI response, and ExportInvoices streams CSV output straight from a projection. In a Vertical Slice Architecture codebase, heavy read models get their own dedicated DbContext or raw SQL queries, keeping them completely isolated from the write-side slices. If a report is slow, you optimize the slice without ever touching order placement.

Cross-cutting concerns — validation, logging, authorization — are handled once in the MediatR pipeline rather than once per slice. A validation behavior runs before every command, and a logging behavior records every request. Slices stay focused on their own behavior because the pipeline handles the rest.

Testing Vertical Slice Architecture Slices

Because every slice is self-contained, tests follow the shape of the code. Each feature folder gets a matching test that exercises the real handler against an in-memory database:

public class CreateCustomerHandlerTests
{
    [Fact]
    public async Task Duplicate_email_is_rejected()
    {
        var db = await TestDb.Create();
        var handler = new CreateCustomerHandler(db, new CustomerValidator());

        var result = await handler.Handle(
            new CreateCustomerCommand("ada@example.com"), CancellationToken.None);

        Assert.False(result.Succeeded);
        Assert.Contains("Email already registered", result.Errors);
    }
}

The test file sits next to the slice it covers, and the test name reads like a requirement from the product backlog. There are no mocks for repositories or services, because there are no repositories or services to mock. The only seam is the database, and swapping it for an in-memory provider is a one-line change.

Key Takeaways

  • A vertical slice is an end-to-end behavior, not a file layout trend
  • CRUD slices stay under 60 lines; workflow slices own their state machines
  • Read models can use a dedicated DbContext without disturbing write slices
  • Tests live beside their slice and run the real handler against an in-memory database
  • The ten slices above mirror the structure of every Indotalent product codebase

Ready to explore a real VSA codebase?

The Indotalent product family ships as .NET 10 applications structured exactly like this walkthrough, slice by slice. Complete .NET 10 source code — $21 each.

Explore Products