ASP.NET Vertical Slice Architecture Template
- Owner
- maidics
- Created
- May 16, 2026
- Last push
- Oct 13, 2026
Lightweight vertical-slice architecture template for ASP.NET Core. Web API only, orchestrated with Aspire. Unit, functional and web testing via TUnit framework. Available on GitHub and NuGet.
This document describes the challenges I faced while working on this topic and how I solved them.
Replacing MediatR
My goal with this template was to provide a lightweight start to building with ASP.NET Core.
Vertical slicing gives developers a simpler, faster workflow than Clean Architecture, though many ideas from Jason Taylor's Clean Architecture Template shaped how this template was built.
Additionally, I wanted to build the template with as few NuGet packages as possible, which meant building a template without MediatR.
Replacing the IRequest and IRequestHandler abstractions turned out to be very easy:
- I created the interfaces myself,
- Added DI registration logic with reflection at startup,
- They can be injected straight into the Minimal API endpoint.
However, with this approach we lose, in my opinion, the most valuable part of MediatR: IPipelineBehavior. Many templates rely on this to do:
- Logging (e.g. per
IRequest, performance issues or uncaught exceptions), - Validation,
- Or even authorization...
ASP.NET Core already has a pipeline system that we can extend: the HTTP request processing pipeline. It can be extended either via a middleware or via IEndpointFilters.
For replacing logging, validation and performance concerns I reached for this abstraction:
LoggingFilter: simple filter that logs incoming HTTP requests; development-only implementation,PerformanceFilter: logs requests that run for longer than 500ms,ValidationFilter:- Resolves all
IValidatorservices (FluentValidation) from DI registered for theIRequestthat is an endpoint method parameter, - This means that if we want automatic validation on the request, the
IRequestobject itself must be an endpoint parameter, - Compared to using the
IPipelineBehavior, my implementation fails faster: the validation runs before endpoint method is invoked.
- Resolves all
I've had custom domain event implementations in previous versions, but they were removed due to multiple reasons:
- Introduced nested database context saves,
- NuGet packages have better tooling for this problem.
Testing structure
I've had some real junior moments when structuring tests; because of this it took many shapes in previous versions:
- v2.1.0: added tests to code shipped with the template such as the
AuditableEntityInterceptororProblemDetailsExceptionHandler, - v2.3.0: separated the template tests into their own project, because they required different testing infrastructure,
- v2.6.0: added a separate unit test project for tests that do not require special setup such as the app host, database etc.,
- v3.0.0:
My goal with v3.0.0 was to vertically slice the tests project and keep everything under one single .csproj that mirrors the main project structure.
This was difficult and messy because different tests require different test infrastructure/dependencies.
As of v4.0.0, I believe I have come up with the final form of the testing structure:
-
UnitTests:- Unit and functional testing,
- Mocked services, in-memory application without server with an Aspire orchestrated DB (from
TestAppHost), - Targets simple classes, extension methods,
IRequestHandlerimplementations.
-
IntegrationTests:- Targets solution infrastructure: code with external dependencies,
- Template provides an empty project for this,
- Does not reference TUnit to prevent exit code 8 (zero tests ran) when running
dotnet test.
-
WebTests:- E2E tests (through endpoints),
- In-memory application with Kestrel, DB provided by Aspire.
This setup covers everything that the template offers with a clean separation.