Skip to content

Latest commit

Β 

History

182 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

SSW Banner

SSW Vertical Slice Architecture Template

SSW TV | YouTube

Package contributions welcome

SSW Vertical Slice Architecture Template

πŸ€” What is it?

An enterprise ready solution template for Vertical Slice Architecture. This template is just one way to apply the Vertical Slice Architecture.

Read more on SSW Rules to Better Vertical Slice Architecture

✨ Features

  • πŸ”¨ dotnet new cli template - to get you started quickly

  • πŸ€– Agent skills - the conventions are executable, not just documented

    • /add-entity and /add-slice ship in .claude/skills/
    • Scaffolds the whole slice, including the strongly typed ID registration that's a startup failure when missed
  • πŸš€ Aspire

    • Dashboard
    • Resource orchestration
    • Observability
    • Simple dev setup - automatic provisioning of database server, schema, and data
  • 🎯 Domain Driven Design Patterns

    • AggregateRoot
    • Entity
    • ValueObject
    • DomainEvent
  • ⚑ FastEndpoints - developer friendly alternative to Minimal APIs.

    • Strongly-typed requests and responses
    • Automatic validation with FluentValidation
    • Support for commands and events
  • πŸ“ OpenAPI/Swagger - easily document your API

  • πŸ”‘ Global Exception Handling - it's important to handle exceptions in a consistent way & protect sensitive information

    • Transforms exceptions into a consistent format following the RFC7231 memo
  • πŸ—„οΈ Entity Framework Core - for data access

  • 🧩 Specification Pattern - abstract EF Core away from your business logic

  • πŸ”€ REPR (Request-Endpoint-Response) Pattern - for structured endpoints

  • πŸ“¦ ErrorOr - fluent result pattern (instead of exceptions)

  • πŸ“¦ FluentValidation - for validating requests

  • πŸ†” Strongly Typed IDs - to combat primitive obsession

    • e.g. pass CustomerId type into methods instead of int, or Guid
    • Entity Framework can automatically convert the int, Guid, nvarchar(..) to strongly typed ID.
  • πŸ“ Directory.Build.Props

    • Consistent build configuration across all projects in the solution
      • e.g. Treating Warnings as Errors for Release builds
    • Custom per project
      • e.g. for all test projects we can ensure that the exact same versions of common packages are referenced
      • e.g. XUnit and NSubstitute packages for all test projects
  • βš–οΈ EditorConfig - comes with the SSW.EditorConfig

  • πŸ§ͺ Testing

  • Architecture Tests

    • Using NetArchTest
    • Know that the team is following the same Vertical Slice Architecture fundamentals
    • The tests are automated so discovering the defects is fast

πŸŽ‰ Getting Started

Prerequisites

  • Docker / Podman / OrbStack
  • .NET 10 SDK
  • Aspire CLI
  • dotnet-ef, restored from the solution's tool manifest:
    dotnet tool restore
    The AppHost's migrations resource shells out to dotnet ef database update on every start, so the app won't boot without it. .config/dotnet-tools.json pins the version that matches the solution's EF Core packages β€” a mismatched global dotnet-ef will not do.

Installing the Template

  1. Install the SSW VSA template
    dotnet new install SSW.VerticalSliceArchitecture.Template

Note

The template only needs to be installed once. Running this command again will update your version of the template.

  1. Create a new directory

    mkdir Sprout
    cd Sprout
  2. Create a new solution

    dotnet new ssw-vsa

Note

name is optional; if you don't specify it, the directory name will be used as the solution name and project namespaces.

Alternatively, you can specify the name and output directory as follows:

dotnet new ssw-vsa --name {{SolutionName}}

Running the Solution

  1. Restore the local tools (first run only)

    dotnet tool restore
  2. Run the solution

    aspire start

Note

The first time you run the solution, it may take a while to download the docker images, create the DB, and seed the data.

  1. Open https://localhost:7255/swagger in your browser to see it running οΈπŸƒβ€β™‚οΈ

Adding Features

Adding a Feature Slice

A full Vertical Slice is a set of files across the domain, persistence, and feature layers:

  • A domain object in src/WebApi/Common/Domain/*
  • Domain configuration in src/WebApi/Common/Persistence/*
  • Command & Query API endpoints in src/WebApi/Features/*

The template ships skills that scaffold all of this for you. In Claude Code:

/add-entity   # domain object, strongly typed ID, spec, EF config, DbSet, Vogen registration, migration
/add-slice    # one use case β€” endpoint, request, response, validator, summary β€” plus tests

Run /add-entity first when the use case needs a domain type that doesn't exist yet, then /add-slice. The skills live in .claude/skills/, and the conventions they follow are documented in CLAUDE.md and .claude/rules/. Using a different agent? Point it at .claude/skills/add-slice/SKILL.md β€” they're plain markdown.

/add-slice adds a slice β€” one use case in its own folder. It creates the Feature and its route Group as well, but only when the slice is the first one in that Feature. CONTEXT.md defines both terms.

To do it by hand instead, copy an existing feature such as Heroes and rename it. Two steps are easy to miss:

  1. Register the strongly typed ID This project uses strongly typed IDs, which require registration in the VogenEfCoreConverters class. Miss this and the app throws on startup β€” it isn't a compile error:

    // Register the newly created Entity ID here
    [EfCoreConverter<PersonId>]
    internal sealed partial class VogenEfCoreConverters;
  2. Add a migration for the new Entity

    dotnet ef migrations add AddPerson --project src/WebApi/WebApi.csproj --startup-project src/WebApi/WebApi.csproj --output-dir Common/Persistence/Migrations

EF Migrations

Migrations are their own Aspire resource. The AppHost declares it with AddEFMigrations("migrations"), and RunDatabaseUpdateOnStart() runs dotnet ef database update before the API starts. Both the migrations and the ApplicationDbContext live in src/WebApi, so every command below targets that one project.

Adding a Migration

dotnet ef migrations add YourMigrationName --project src/WebApi/WebApi.csproj --startup-project src/WebApi/WebApi.csproj --output-dir Common/Persistence/Migrations

Nothing needs to be running for this β€” no database, no AppHost. Scaffolding a migration only needs the model.

Applying a Migration

Locally, .NET Aspire handles this for you β€” just start the project. The migrations resource runs to completion, then the seeder and api resources start. Watch its progress in the Aspire Dashboard like any other resource.

On Azure this is a deployment step rather than something the app does to itself β€” see Deploying to Azure.

Removing a Migration

dotnet ef migrations remove --project src/WebApi/WebApi.csproj --startup-project src/WebApi/WebApi.csproj --force

--force is always required here, and not because anything needs to be running. Aspire injects the connection string into the migrations and api resources at runtime, so a dotnet ef you run yourself never gets one. EF can't reach the database to check whether the migration has been applied, and refuses to guess β€” without --force it stops at The ConnectionString property has not been initialized.

--force skips that check, so mind what the check was for. If you've started the app since adding the migration, the migrations resource has already applied it, and deleting the files leaves the database ahead of the model. Recover by dropping the local database β€” the Drop Database command on the AppDb resource, from the dashboard or via aspire resource AppDb drop-database β€” or by rolling forward with a new migration that undoes the change. Rolling forward is the safer habit once a migration has left your machine.

Deploying to Azure

The template can be deployed to Azure via the Azure Developer CLI (AZD). This will setup the following:

  • Azure App Service: API
  • Azure SQL Server + Database: Data storage
  • Application Insights + Log Analytics: For monitoring and logging
  • Managed Identities: For secure access to Azure resources
  • Azure Container Registry: For storing Docker images

The seeder resource is deliberately absent. It's only added to the graph in run mode, so it never reaches Azure and Bogus data can't land in a deployed database.

Steps to Deploy

  1. Authenticate with Azure

    azd auth login
  2. Initialize AZD for the project

    azd init
  3. Deploy to Azure

    azd up

Note

azd up combines azd provision and azd deploy commands to create the resources and deploy the application. If running this from a CI/CD pipeline, you can use azd provision and azd deploy separately in the appropriate places.

Applying Migrations on Azure

azd up does not apply migrations. PublishAsMigrationBundle() writes an artifact; it doesn't run one. Applying it is a step you own.

Publishing produces a self-contained EF Core migration bundle at efmigrations/migrations in the output directory. Run it against the target database as a deployment step, after azd provision and before (or alongside) azd deploy:

aspire publish --output-path ./publish
./publish/efmigrations/migrations --connection "<target-connection-string>"

Important

The bundle is a native executable built for the platform that published it. Publishing on a macOS or Windows dev box produces a binary that will not run on a Linux CI agent. Generate it on a runner matching wherever you intend to execute it.

Two alternatives, depending on how your organisation prefers to ship schema changes:

  • PublishAsMigrationScript() in place of PublishAsMigrationBundle() emits an idempotent .sql script instead of a binary. It has no platform problem and it's reviewable before it runs, which many DBA-gated environments require.
  • PublishAsAzureContainerAppJob() is the one option that applies migrations automatically on deploy, but it needs Azure Container Apps. This template targets App Service, so it isn't wired up here.

πŸŽ“ Learn More

Vertical Slice Architecture: How Does it Compare to Clean Architecture | .NET Conf 2023

graph TD;
    subgraph ASP.NET Core Web App
        subgraph Slices
            A[Feature A]
            B[Feature B]
        end
        Slices --> |depends on| Common
        Host --> |depends on| Common
        Host --> |depends on| Slices
        ASPNETCore[ASP.NET Core] --> |uses| Host
    end

    Common[Common]
Loading

πŸš€ Publishing Template

Template will be published to NuGet.org when changes are made to VerticalSliceArchitecture.nuspec on the main branch.

Process

  1. Update the version attribute in VerticalSliceArchitecture.nuspec
  2. Merge your PR
  3. package GitHub Action will run and publish the new version to NuGet.org
  4. Create a GitHub release to document the changes

Note

We are now using CalVer for versioning. The version number should be in the format YYYY.M.D (e.g. 2024.2.12).

🀝 Contributing

Contributions, issues and feature requests are welcome! See Contributing for more information.

About

An enterprise ready solution template for Vertical Slice Architecture. This template is just one way to apply the Vertical Slice Architecture.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

394 stars

Watchers

3 watching

Forks

Releases

Used by

Contributors

Languages