Skip to content

Quickstart

Choose the path that matches your first use case. Each path is designed to be copy-paste friendly with minimal changes to the template.

Option 1: add caching (5 minutes)

1) Install packages

dotnet add src/Application/Application.csproj package CleanArchitecture.Extensions.Caching
dotnet add src/Infrastructure/Infrastructure.csproj package CleanArchitecture.Extensions.Caching

2) Register caching services (Infrastructure)

using CleanArchitecture.Extensions.Caching;

builder.Services.AddCleanArchitectureCaching();

3) Add the MediatR caching behavior (Application)

builder.Services.AddMediatR(cfg =>
{
    cfg.RegisterServicesFromAssemblyContaining<Program>();
    cfg.AddCleanArchitectureCachingPipeline();
});

4) Verify

Run the same query twice and confirm the second request is served from cache (debug logs show cache hit/miss).

Option 2: add multitenancy to an HTTP API (10 minutes)

1) Install packages

dotnet add src/Application/Application.csproj package CleanArchitecture.Extensions.Multitenancy
dotnet add src/Infrastructure/Infrastructure.csproj package CleanArchitecture.Extensions.Multitenancy
dotnet add src/Web/Web.csproj package CleanArchitecture.Extensions.Multitenancy.AspNetCore

2) Register services and middleware

using CleanArchitecture.Extensions.Multitenancy.AspNetCore;

builder.Services.AddCleanArchitectureMultitenancyAspNetCore(autoUseMiddleware: true);

var app = builder.Build();

If you prefer manual wiring, call app.UseCleanArchitectureMultitenancy() instead of enabling autoUseMiddleware. Use manual wiring when you need claim- or route-based resolution so you can place the middleware after authentication or routing.

Template-friendly default: use header or host resolution. Route-based extraction requires routing middleware before the multitenancy middleware.

3) Add the multitenancy pipeline behaviors

builder.Services.AddMediatR(cfg =>
{
    cfg.RegisterServicesFromAssemblyContaining<Program>();
    cfg.AddCleanArchitectureMultitenancyPipeline();
});

Keep the multitenancy pipeline after authorization behaviors in the Jason Taylor template so authorization runs first.

4) Mark tenant-required endpoints

using CleanArchitecture.Extensions.Multitenancy.AspNetCore.Routing;

app.MapGroup("/tenants/{tenantId}")
    .AddTenantEnforcement()
    .RequireTenant();

Optional: add EF Core isolation

dotnet add src/Infrastructure/Infrastructure.csproj package CleanArchitecture.Extensions.Multitenancy.EFCore
using CleanArchitecture.Extensions.Multitenancy.EFCore;
using CleanArchitecture.Extensions.Multitenancy.EFCore.Options;

builder.Services.AddCleanArchitectureMultitenancyEfCore(options =>
{
    options.Mode = TenantIsolationMode.SharedDatabase;
    options.TenantIdPropertyName = "TenantId";
    options.UseShadowTenantId = true;
});

Row-level filtering/enforcement defaults to shared database mode. For schema/database-per-tenant setups, explicitly set UseShadowTenantId, EnableQueryFilters, and EnableSaveChangesEnforcement to true if you want row-level defense-in-depth.

Next steps: - Installation guide - Caching extension - Multitenancy core - Multitenancy.AspNetCore - Multitenancy.EFCore