Skip to main content
The FullStackHero starter kit follows a modular monolith architecture with clear separation between reusable building blocks, domain modules, and application hosts.

Repository Overview

.
├── src/
   ├── BuildingBlocks/        # Framework (11 packages)
   ├── Modules/               # Business features (3 core modules)
   ├── Playground/            # Reference application
   ├── Tests/                 # Architecture + unit tests
   ├── Tools/                 # CLI utilities
   ├── Directory.Build.props  # Global MSBuild properties
   ├── Directory.Packages.props  # Centralized package versions
   └── FSH.Framework.slnx     # Solution file
├── docs/                      # Documentation (this site)
├── terraform/                 # Infrastructure as Code
└── scripts/                   # Build/deployment scripts

Build Configuration

The repository uses Central Package Management to ensure consistent versions across all projects.

Directory.Build.props

Global MSBuild properties applied to all projects:
src/Directory.Build.props
<Project>
  <PropertyGroup>
    <!-- Target framework -->
    <TargetFramework>net10.0</TargetFramework>

    <!-- Enable latest C# features -->
    <LangVersion>latest</LangVersion>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>

    <!-- Code quality -->
    <EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
    <AnalysisLevel>latest</AnalysisLevel>
    <AnalysisMode>AllEnabledByDefault</AnalysisMode>

    <!-- Versioning -->
    <Version>10.0.0-rc.1</Version>
    <Authors>Mukesh Murugan</Authors>
    <Company>FullStackHero</Company>
  </PropertyGroup>
</Project>
Key features:
  • net10.0: All projects target .NET 10
  • Nullable reference types enabled by default
  • Implicit usings for common namespaces
  • Code analysis enforced at build time

Directory.Packages.props

Centralized package version management (CPM):
src/Directory.Packages.props (excerpt)
<Project>
  <PropertyGroup>
    <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
  </PropertyGroup>
  
  <ItemGroup>
    <PackageVersion Include="Microsoft.EntityFrameworkCore" Version="10.0.2" />
    <PackageVersion Include="Finbuckle.MultiTenant" Version="10.0.2" />
    <PackageVersion Include="Mediator.Abstractions" Version="3.0.1" />
    <PackageVersion Include="Hangfire.Core" Version="1.8.23" />
    <!-- ... 100+ packages -->
  </ItemGroup>
</Project>
Projects reference packages without versions:
Example: Playground.Api.csproj
<ItemGroup>
  <PackageReference Include="Microsoft.EntityFrameworkCore.Design" />
  <PackageReference Include="Mediator.Abstractions" />
</ItemGroup>

src/BuildingBlocks/

Reusable framework packages providing cross-cutting concerns. These are protected and should not be modified unless extending the framework.
src/BuildingBlocks/
├── Core/                      # DDD primitives, exceptions, context
├── Persistence/               # EF Core, repository, specifications
├── Caching/                   # Redis + in-memory caching
├── Eventing/                  # Domain events, inbox/outbox
├── Eventing.Abstractions/     # Event contracts
├── Jobs/                      # Hangfire background jobs
├── Mailing/                   # SMTP + SendGrid email
├── Storage/                   # Local/S3/Azure file storage
├── Shared/                    # DTOs, shared contracts
├── Web/                       # ASP.NET Core host, auth, validation
├── Blazor.UI/                 # Blazor components
└── (11 total packages)

Core

Domain-Driven Design primitives and abstractions:
BuildingBlocks/Core/
├── Abstractions/              # IAppUser, IClock, etc.
├── Context/                   # ICurrentUser, IRequestContext
├── Domain/                    # BaseEntity, AggregateRoot, ValueObject
├── Exceptions/                # Custom exception types
└── Common/                    # QueryStringKeys, constants
Key types:
  • BaseEntity<TId>: Base class for entities with ID and audit fields
  • AggregateRoot<TId>: Root entity with domain events
  • ValueObject: Base class for value objects
  • ICurrentUser: Access to authenticated user claims

Persistence

Entity Framework Core infrastructure:
BuildingBlocks/Persistence/
├── Context/                   # DbContext base classes
├── Inteceptors/               # EF Core interceptors (audit, events)
├── Specifications/            # Repository specification pattern
├── Pagination/                # PaginatedList<T>
└── Repository/                # IRepository<T>, IReadRepository<T>
Key features:
  • BaseDbContext: Multi-tenant aware DbContext
  • IRepository<T>: Generic repository with specification support
  • Specification<T>: Reusable query specifications
  • Audit interceptor: Auto-populates CreatedBy, ModifiedBy, etc.

Caching

Distributed and in-memory caching:
BuildingBlocks/Caching/
├── CacheService.cs            # IDistributedCache wrapper
├── MemoryCacheService.cs      # IMemoryCache wrapper
└── Extensions.cs              # DI registration
Usage:
public class MyHandler(ICacheService cache)
{
    public async Task<Data> Handle(Query q, CancellationToken ct)
    {
        return await cache.GetOrSetAsync(
            $"data:{q.Id}",
            () => _repo.GetByIdAsync(q.Id, ct),
            TimeSpan.FromMinutes(5),
            ct);
    }
}

Web

ASP.NET Core host infrastructure:
BuildingBlocks/Web/
├── Auth/                      # JWT bearer authentication
├── Cors/                      # CORS configuration
├── Exceptions/                # Global exception handling
├── Health/                    # Health check endpoints
├── Mediator/                  # Mediator integration
├── Modules/                   # Module loader
├── Observability/             # OpenTelemetry setup
├── OpenApi/                   # Swagger/Scalar configuration
├── RateLimiting/              # Rate limiting policies
├── Security/                  # Security headers, CSP
├── Validation/                # FluentValidation pipeline
└── Versioning/                # API versioning
Key method:
builder.AddHeroPlatform(o =>
{
    o.EnableCaching = true;
    o.EnableMailing = true;
    o.EnableJobs = true;
});
This single call wires:
  • JWT authentication
  • CORS
  • OpenAPI
  • Health checks
  • Rate limiting
  • OpenTelemetry
  • Exception handling
  • Validation

src/Modules/

Business features organized as bounded contexts. Each module is self-contained with its own:
  • Domain models
  • Database context
  • API endpoints
  • Contracts (DTOs)
src/Modules/
├── Identity/
   ├── Modules.Identity/              # Runtime implementation
   ├── Authorization/             # JWT, permissions, roles
   ├── Data/                      # IdentityDbContext, configurations
   ├── Domain/                    # User, Role entities
   ├── Features/v1/               # CQRS features
   ├── Tokens/TokenGeneration/
   ├── Users/CreateUser/
   ├── Roles/CreateRole/
   └── ...
   ├── Services/                  # UserService, RoleService
   └── IdentityModule.cs          # Module registration
   └── Modules.Identity.Contracts/    # Public DTOs/interfaces
       └── v1/Tokens/TokenGeneration/
├── Multitenancy/
   ├── Modules.Multitenancy/
   ├── Data/                      # TenantDbContext
   ├── Domain/                    # Tenant entity
   ├── Features/v1/
   ├── GetTenantStatus/
   ├── CreateTenant/
   └── ...
   └── MultitenancyModule.cs
   └── Modules.Multitenancy.Contracts/
└── Auditing/
    ├── Modules.Auditing/
   ├── Persistence/               # AuditDbContext
   ├── Features/v1/
   ├── GetAuditTrail/
   └── ...
   └── AuditingModule.cs
    └── Modules.Auditing.Contracts/

Module Pattern

Each module follows vertical slice architecture:
Modules/{Module}/Features/v1/{Feature}/
├── {Action}{Entity}Command.cs         # ICommand<T> or IQuery<T>
├── {Action}{Entity}Handler.cs         # ICommandHandler<T,R>
├── {Action}{Entity}Validator.cs       # AbstractValidator<T>
└── {Action}{Entity}Endpoint.cs        # Minimal API endpoint
Example: CreateUser feature
Identity/Features/v1/Users/CreateUser/
├── CreateUserCommand.cs
├── CreateUserHandler.cs
├── CreateUserValidator.cs
└── CreateUserEndpoint.cs
Each feature is a complete vertical slice containing:
  • Request/response contracts
  • Business logic
  • Validation rules
  • HTTP endpoint mapping

Module Registration

Modules self-register via IModule interface:
src/Modules/Identity/IdentityModule.cs
public sealed class IdentityModule : IModule
{
    public void RegisterModule(IServiceCollection services, IConfiguration config)
    {
        // Register module-specific services
        services.AddDbContext<IdentityDbContext>();
        services.AddScoped<IUserService, UserService>();
        services.AddIdentityCore<ApplicationUser>();
        // ...
    }

    public void UseModule(IApplicationBuilder app)
    {
        // Configure middleware if needed
    }

    public void MapEndpoints(IEndpointRouteBuilder endpoints)
    {
        var group = endpoints.MapGroup("/api/v1/identity");
        // Map endpoints from Features/
        group.MapPost("/tokens", GenerateTokenEndpoint.Handle);
        group.MapPost("/users", CreateUserEndpoint.Handle);
        // ...
    }
}

Contracts Projects

Each module has a separate Contracts project containing:
  • DTOs (Data Transfer Objects)
  • Request/Response models
  • Public interfaces
This allows other modules to reference contracts without taking a dependency on the full module implementation.
Modules.Identity.Contracts/v1/Tokens/
├── TokenGeneration/
   ├── GenerateTokenCommand.cs        # Shared contract
   └── TokenResponse.cs               # Shared response

src/Playground/

Reference implementation demonstrating how to use the framework:
src/Playground/
├── Playground.Api/                    # ASP.NET Core Web API
   ├── Program.cs                     # Application entry point
   ├── appsettings.json               # Configuration
   └── wwwroot/uploads/               # Local file storage
├── Playground.Blazor/                 # Blazor WebAssembly UI
   ├── Pages/                         # Blazor pages
   ├── Services/                      # API client services
   └── Program.cs
├── Migrations.PostgreSQL/             # EF Core migrations (PostgreSQL)
├── Migrations.SqlServer/              # EF Core migrations (SQL Server)
└── FSH.Playground.AppHost/            # .NET Aspire orchestration
    └── AppHost.cs                     # Container orchestration

Playground.Api

Minimal Program.cs demonstrating module composition:
src/Playground/Playground.Api/Program.cs
var builder = WebApplication.CreateBuilder(args);

// Validate required configuration in Production
if (builder.Environment.IsProduction())
{
    Require(builder.Configuration, "DatabaseOptions:ConnectionString");
    Require(builder.Configuration, "CachingOptions:Redis");
    Require(builder.Configuration, "JwtOptions:SigningKey");
}

// Register Mediator with module assemblies
builder.Services.AddMediator(o =>
{
    o.ServiceLifetime = ServiceLifetime.Scoped;
    o.Assemblies = [
        typeof(GenerateTokenCommand),
        typeof(GenerateTokenCommandHandler),
        typeof(GetTenantStatusQuery),
        typeof(GetTenantStatusQueryHandler),
        typeof(FSH.Modules.Auditing.Contracts.AuditEnvelope),
        typeof(FSH.Modules.Auditing.Persistence.AuditDbContext)];
});

// Module assemblies
var moduleAssemblies = new Assembly[]
{
    typeof(IdentityModule).Assembly,
    typeof(MultitenancyModule).Assembly,
    typeof(AuditingModule).Assembly
};

// Add framework + modules
builder.AddHeroPlatform(o =>
{
    o.EnableCaching = true;
    o.EnableMailing = true;
    o.EnableJobs = true;
});
builder.AddModules(moduleAssemblies);

var app = builder.Build();

// Apply tenant migrations
app.UseHeroMultiTenantDatabases();

// Use framework + map module endpoints
app.UseHeroPlatform(p =>
{
    p.MapModules = true;
    p.ServeStaticFiles = true;
});

// Custom endpoint
app.MapGet("/", () => Results.Ok(new { message = "hello world!" }))
   .WithTags("PlayGround")
   .AllowAnonymous();

await app.RunAsync();
The entire API is configured in 70 lines thanks to the modular framework design.

Migrations Projects

Separate projects for each database provider:
  • Migrations.PostgreSQL: Contains all EF Core migrations for PostgreSQL (Npgsql provider)
  • Migrations.SqlServer: Contains migrations for SQL Server
This allows switching providers by changing:
  1. DatabaseOptions__Provider config
  2. DatabaseOptions__MigrationsAssembly config

src/Tests/

Architecture and unit tests:
src/Tests/
├── Architecture.Tests/                # NetArchTest rules
   ├── LayerTests.cs                  # Enforce layer dependencies
   ├── ModuleTests.cs                 # Module boundary checks
   └── NamingTests.cs                 # Naming conventions
├── Modules.Identity.Tests/            # Identity module tests
├── Modules.Multitenancy.Tests/        # Multitenancy tests
└── BuildingBlocks.Tests/              # Framework tests

Architecture Tests

Enforce architectural rules at build time:
[Fact]
public void Modules_Should_Not_Reference_Other_Modules()
{
    var result = Types.InAssembly(typeof(IdentityModule).Assembly)
        .Should()
        .NotHaveDependencyOn("FSH.Modules.Multitenancy")
        .GetResult();

    Assert.True(result.IsSuccessful);
}

src/Tools/

CLI utilities for developers:
src/Tools/
└── CLI/                               # Command-line tool
    ├── Commands/                      # Spectre.Console commands
    └── Program.cs
Usage:
dotnet run --project src/Tools/CLI -- generate:module Catalog
dotnet run --project src/Tools/CLI -- generate:feature CreateProduct

Key Principles

Vertical Slices

Each feature is a complete slice from request to response

Module Isolation

Modules reference contracts, not implementations

Framework Separation

BuildingBlocks are protected from business logic

Configuration Over Code

Behavior controlled via appsettings.json
To understand…Explore…
DDD primitivessrc/BuildingBlocks/Core/Domain/
EF Core setupsrc/BuildingBlocks/Persistence/
Authenticationsrc/Modules/Identity/Authorization/
Multi-tenancysrc/Modules/Multitenancy/
CQRS patternsrc/Modules/*/Features/v1/
API hostingsrc/BuildingBlocks/Web/
Module loadingsrc/BuildingBlocks/Web/Modules/
Configurationsrc/Playground/Playground.Api/appsettings.json

Next Steps

Architecture

Deep dive into modular monolith architecture

Modules

Learn about core modules

CQRS

Implement commands and queries

Add Feature

Create your first feature slice

Build docs developers (and LLMs) love