TheAppManager 2.0.0
dotnet add package TheAppManager --version 2.0.0
NuGet\Install-Package TheAppManager -Version 2.0.0
<PackageReference Include="TheAppManager" Version="2.0.0" />
<PackageVersion Include="TheAppManager" Version="2.0.0" />
<PackageReference Include="TheAppManager" />
paket add TheAppManager --version 2.0.0
#r "nuget: TheAppManager, 2.0.0"
#:package TheAppManager@2.0.0
#addin nuget:?package=TheAppManager&version=2.0.0
#tool nuget:?package=TheAppManager&version=2.0.0
<p align="center"> <img src="logo.png" alt="TheAppManager" width="128" /> </p>
TheAppManager
A lightweight, composable module system for ASP.NET Core application startup.
Table of Contents
- Overview
- Installation
- Quick Start
- Creating Modules
- Auto-Discovery
- Multi-Assembly Scanning
- Testing
- Async Support
- Builder Configuration Hook
- Advanced: Using AppManagerBuilder
- Project Structure
- Contributing
- License
Overview
TheAppManager lets you organize your ASP.NET Core startup into independent, composable modules. Each module encapsulates its own services, middleware, and endpoints — keeping Program.cs clean and making features reusable across projects.
Architecture
┌──────────────────────┐
│ AppManager.Start() │
└──────────┬───────────┘
│
┌──────────▼───────────┐
│ AppManagerBuilder │
│ ┌─────────────────┐ │
│ │ WebApplication │ │
│ │ Builder │ │
│ └─────────────────┘ │
└──────────┬───────────┘
│ applies in registration order
┌────────────────┼────────────────┐
│ │ │
┌─────────▼──────┐ ┌──────▼───────┐ ┌──────▼───────┐
│ SwaggerModule │ │ AuthModule │ │ WeatherModule│
│ ├ Services │ │ ├ Services │ │ ├ Services │
│ └ Middleware │ │ └ Middleware │ │ └ Endpoints │
└────────────────┘ └──────────────┘ └──────────────┘
Why Modules?
As ASP.NET Core applications grow, Program.cs accumulates unrelated service registrations, middleware, and endpoint mappings. Modules solve this by letting each feature own its startup logic:
- Composable — add, remove, or reorder modules without touching other code
- Reusable — package a module in a NuGet library and share it across projects
- Testable — each module can be tested in isolation with
AppModuleTestHost - Transparent ordering — modules run in the order you register them, just like middleware
Features
- Composable modules — encapsulate services, middleware, and endpoints into independent
IAppModuleimplementations - Registration-order execution — modules are applied in the order you add them, no magic priority numbers
- Auto-discovery — automatically find and register all
IAppModuleimplementations in your assembly - Multi-assembly scanning — discover modules from referenced assemblies with
AddFromAssemblyOf<T>() - Conditional registration — add modules only when conditions are met with
AddIf<T>(bool) - Module replacement — swap modules for test doubles with
Replace<TOld, TNew>() - Test host —
AppModuleTestHostbuilds a TestServer from modules for integration testing - Fluent API — chain module registrations for readable startup code
- Async support — run applications asynchronously with
StartAsyncandRunAsync - Builder hook — customize
WebApplicationBuilderbefore modules are applied - Zero external dependencies — built solely on
Microsoft.AspNetCore.Appframework reference
Installation
dotnet add package TheAppManager
Quick Start
Register modules explicitly in Program.cs:
using TheAppManager.Startup;
AppManager.Start(args, modules =>
{
modules
.Add<SwaggerModule>()
.Add<AuthModule>()
.Add<WeatherModule>();
});
Or let TheAppManager discover modules automatically:
using TheAppManager.Startup;
AppManager.Start(args);
Creating Modules
A Minimal Module
Implement IAppModule and override only what you need. All methods have default no-op implementations:
using TheAppManager.Modules;
public class SwaggerModule : IAppModule
{
public void ConfigureServices(WebApplicationBuilder builder)
{
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
}
public void ConfigureMiddleware(WebApplication app)
{
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
}
}
A module that only registers endpoints:
public class WeatherModule : IAppModule
{
public void ConfigureServices(WebApplicationBuilder builder)
{
builder.Services.AddScoped<WeatherForecastService>();
}
public void ConfigureEndpoints(IEndpointRouteBuilder endpoints)
{
endpoints.MapGet("/weatherforecast", (WeatherForecastService svc) =>
Results.Ok(svc.GetForecasts()));
}
}
Note that ConfigureServices receives WebApplicationBuilder (not just IServiceCollection), giving you access to builder.Configuration, builder.Environment, and builder.Host when you need them.
Module Ordering
Modules are applied in registration order — the order you call Add<T>(). This is the same principle as ASP.NET Core middleware: what you register first runs first.
AppManager.Start(args, modules =>
{
modules
.Add<SwaggerModule>() // services & middleware first
.Add<AuthModule>() // auth middleware second
.Add<WeatherModule>(); // endpoints last
});
Conditional Modules
Use AddIf<T>() to register modules based on runtime conditions:
var isDev = builder.Environment.IsDevelopment();
AppManager.Start(args, modules =>
{
modules
.AddIf<SwaggerModule>(isDev)
.Add<AuthModule>()
.Add<WeatherModule>();
});
Auto-Discovery
When you call AppManager.Start(args) without a configure callback, TheAppManager scans the entry assembly for all concrete classes implementing IAppModule with a parameterless constructor and registers them automatically (sorted alphabetically by type name for deterministic ordering).
This is convenient for applications where module ordering doesn't matter, or where you're fine with alphabetical ordering.
// Discovers and registers all IAppModule implementations in the entry assembly
AppManager.Start(args);
For order-sensitive applications, prefer explicit registration.
Multi-Assembly Scanning
When your modules live in referenced libraries, scan their assemblies explicitly:
AppManager.Start(args, modules =>
{
// Scan the assembly containing SwaggerModule for all modules
modules.AddFromAssemblyOf<SwaggerModule>();
// Or scan a specific assembly
modules.AddFromAssembly(typeof(SomeLibraryModule).Assembly);
// Mix with explicit registration
modules.Add<MyAppModule>();
});
Testing
Integration Testing with AppModuleTestHost
AppModuleTestHost builds a TestServer from your modules, making integration tests simple:
using TheAppManager.Testing;
[Fact]
public async Task WeatherEndpoint_ReturnsForecasts()
{
await using var host = await new AppModuleTestHost()
.Add<WeatherModule>()
.StartAsync();
var client = host.GetTestClient();
var response = await client.GetAsync("/weatherforecast");
response.StatusCode.ShouldBe(HttpStatusCode.OK);
}
You can also resolve services directly:
await using var host = await new AppModuleTestHost()
.Add<WeatherModule>()
.StartAsync();
var service = host.GetRequiredService<WeatherForecastService>();
Replacing Modules in Tests
Use Replace<TOld, TNew>() to swap a real module for a test double:
public class FakeAuthModule : IAppModule
{
public void ConfigureServices(WebApplicationBuilder builder)
{
builder.Services.AddSingleton<IAuthService, FakeAuthService>();
}
}
var modules = new AppModuleCollection()
.Add<AuthModule>()
.Add<WeatherModule>()
.Replace<AuthModule, FakeAuthModule>();
Async Support
Use StartAsync for async startup:
await AppManager.StartAsync(args, modules =>
{
modules
.Add<SwaggerModule>()
.Add<WeatherModule>();
});
Builder Configuration Hook
Customize the WebApplicationBuilder before modules are applied:
AppManager.Start(
args,
modules =>
{
modules
.Add<SwaggerModule>()
.Add<WeatherModule>();
},
builder =>
{
builder.Configuration.AddJsonFile("custom-settings.json", optional: true);
});
Advanced: Using AppManagerBuilder
For full control, use AppManagerBuilder directly:
using TheAppManager.Modules;
using TheAppManager.Startup;
var modules = new AppModuleCollection()
.Add<SwaggerModule>()
.Add<AuthModule>()
.Add<WeatherModule>();
var appManager = new AppManagerBuilder(args)
.ConfigureBuilder(builder =>
{
builder.Configuration.AddJsonFile("custom-settings.json");
})
.Build(modules);
await appManager.RunAsync();
Project Structure
src/TheAppManager/ → Class library (NuGet package)
samples/TheAppManager.Sample/ → Sample web app demonstrating usage
tests/TheAppManager.Tests/ → Unit and integration tests
Contributing
- Fork the repository
- Create a feature branch (
git checkout -b feature/my-feature) - Make your changes
- Run tests (
dotnet test) - Commit your changes (
git commit -am 'Add my feature') - Push to the branch (
git push origin feature/my-feature) - Open a Pull Request
License
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.AspNetCore.TestHost (>= 10.0.3)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 2.0.0 | 270 | 3/10/2026 |
v2.0.0: Complete redesign — composable module system replacing the strategy pattern.