| name | define-application-modules |
| description | Define and wire up an ABP module: the AbpModule class, [DependsOn] dependencies, service registration, options configuration, lifecycle hooks, and conventional DI. USE FOR: creating an AbpModule class, declaring [DependsOn] dependencies, registering services in ConfigureServices / PreConfigureServices / PostConfigureServices, configuring options with Configure for an options class, adding startup/shutdown lifecycle hooks (OnApplicationInitialization / OnApplicationShutdown), relying on ITransientDependency/IScopedDependency/ISingletonDependency conventional DI. DO NOT USE FOR: deciding which project a class belongs in or the overall solution layering (use layered-architecture); modeling entities/aggregates/value objects/domain services (use model-domain-aggregates); loading modules at runtime as plug-ins (use create-plugin-modules); writing application service implementations (use application-services).
|
| license | MIT |
Defining ABP Modules
Every ABP application and reusable module is defined by a module class that derives from AbpModule (namespace Volo.Abp.Modularity). The module class is where you declare dependencies, register services, configure options, and run startup/shutdown logic. ABP builds the dependency graph from the root module and initializes/shuts down all modules in the correct order.
When to Use
- Creating an
AbpModule class for an application or reusable module.
- Declaring dependencies on other modules with
[DependsOn].
- Registering services in
ConfigureServices (or Pre/PostConfigureServices).
- Configuring options with
Configure<TOptions> and its Pre/Post variants.
- Adding startup/shutdown lifecycle hooks.
- Wiring up conventional DI via
ITransientDependency / IScopedDependency / ISingletonDependency.
When Not to Use
- Deciding which project/layer a class belongs in — use the layered-architecture skill.
- Modeling entities, aggregate roots, value objects, or domain services — use the model-domain-aggregates skill.
- Loading modules at runtime as plug-ins (
PlugInSources) — use the create-plugin-modules skill.
- Writing application service implementations — use the application-services skill.
How it works
The Module Class
using Volo.Abp.Modularity;
public class BlogModule : AbpModule
{
}
AbpModule is abstract and already implements all the lifecycle interfaces, so you only override the methods you need. Module classes are registered into DI as singletons.
Declaring Dependencies with [DependsOn]
Declare only your direct dependencies; ABP resolves the transitive graph.
[DependsOn(typeof(AbpAspNetCoreMvcModule))]
[DependsOn(typeof(AbpAutofacModule))]
public class BlogModule : AbpModule
{
}
You can use multiple [DependsOn] attributes or pass multiple types to one attribute. After abp add-package <package>, add the corresponding module type to [DependsOn] (the CLI does this for you).
Service Registration & Options
ConfigureServices is the main place to register services and configure other modules. It receives a ServiceConfigurationContext whose Services property is the IServiceCollection.
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddTransient<IExternalLogger, AzureExternalLogger>();
Configure<AbpDbConnectionOptions>(options =>
{
options.ConnectionStrings.Default = "...";
});
}
AbpModule exposes protected option helpers that wrap the standard options system:
Configure<TOptions>(...), Configure<TOptions>(string name, ...), Configure<TOptions>(IConfiguration), plus PreConfigure<TOptions>, PostConfigure<TOptions> and PostConfigureAll<TOptions>.
Pre / Post Configure Services
PreConfigureServices runs before, and PostConfigureServices after, the ConfigureServices methods of all modules. Use PreConfigureServices for things other modules must see early (e.g. context.Services.OnRegistered(...) interceptor hooks, or PreConfigure<IMvcBuilder>(...)).
public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.OnRegistered(ctx =>
{
});
}
Async Versions
Each of these has an async counterpart: ConfigureServicesAsync, PreConfigureServicesAsync, PostConfigureServicesAsync. Override the async version only when you need await inside. AbpModule's default async implementation calls the sync one, so on an async startup path (CreateAsync / InitializeAsync) the async method runs, while a synchronous startup (Create / Initialize) runs the sync method directly. If you override only the async version, the synchronous startup path skips your custom logic — override both (or the sync version) if the app may start either way. The same rule applies to all lifecycle methods below.
Application Lifecycle Hooks
Once all services are configured, ABP initializes modules. At this point IServiceProvider is ready, so you can resolve services.
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var myService = context.ServiceProvider.GetRequiredService<MyService>();
myService.DoSomething();
}
The startup module typically builds the ASP.NET Core middleware pipeline here:
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var app = context.GetApplicationBuilder();
var env = context.GetEnvironment();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseConfiguredEndpoints();
}
Available hooks (each with an ...Async counterpart, receiving ApplicationInitializationContext or ApplicationShutdownContext):
OnPreApplicationInitialization — before OnApplicationInitialization of all modules
OnApplicationInitialization — main initialization
OnPostApplicationInitialization — after all modules initialized
OnApplicationShutdown — cleanup on shutdown
Conventional Dependency Injection
ABP scans the main assembly (the assembly that defines the module class) and auto-registers services. Some base types are registered by convention (e.g. application services deriving from ApplicationService and domain services deriving from DomainService register as transient).
For your own classes, implement a lifetime interface (namespace Volo.Abp.DependencyInjection):
public class TaxCalculator : ITransientDependency { }
public class CacheManager : ISingletonDependency { }
public class RequestState : IScopedDependency { }
By convention a class exposes itself plus its default interfaces (interfaces whose name matches by convention, e.g. TaxCalculator → ITaxCalculator). Use [ExposeServices(...)] to restrict, and [Dependency(...)] (Lifetime, TryRegister, ReplaceServices) for finer control.
Additional & Skipped Registration
- Set
SkipAutoServiceRegistration = true in the module constructor to disable auto-scanning; then register manually, e.g. context.Services.AddAssemblyOf<BlogModule>().
- Use
[AdditionalAssembly(typeof(SomeTypeInOtherAssembly))] on the module only in the rare case a single module spans multiple assemblies. Prefer a separate module + [DependsOn] instead.
The options pattern (producer + consumer)
ABP builds on Microsoft.Extensions.Options. A producer module defines a plain options class and lets dependents configure it; a consumer injects IOptions<TOptions> and reads .Value at runtime.
Configure and consume
Define a plain class (the producer's contract), let any dependent module set it in ConfigureServices, and read it where you need it:
public class BlogOptions
{
public int PageSize { get; set; }
public bool EnableComments { get; set; }
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<BlogOptions>(options =>
{
options.PageSize = 25;
options.EnableComments = true;
});
}
Configure<TOptions>(...) is the AbpModule shortcut for context.Services.Configure<TOptions>(...) (both work). Consume at runtime by injecting IOptions<TOptions> and reading its .Value:
public class BlogService : ITransientDependency
{
private readonly BlogOptions _options;
public BlogService(IOptions<BlogOptions> options)
{
_options = options.Value;
}
}
IOptions<TOptions> is a singleton with a fixed snapshot; use IOptionsSnapshot<TOptions> (scoped) or IOptionsMonitor<TOptions> when you need reload-aware or per-scope values — same as standard .NET.
Pre-configuration: influencing options before DI is finalized
IOptions<TOptions>.Value is only readable after all modules' ConfigureServices complete. But a producer module sometimes needs option values during registration — to decide what to register or how to wire other services. That's what pre-configuration is for.
The producer exposes a pre-options class. Dependents set it in PreConfigureServices with PreConfigure<TOptions>(...), and the producer reads the accumulated values back in its own ConfigureServices via context.Services.ExecutePreConfiguredActions<TOptions>():
public class MyPreOptions
{
public bool MyValue { get; set; }
}
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<MyPreOptions>(options =>
{
options.MyValue = true;
});
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
var options = context.Services.ExecutePreConfiguredActions<MyPreOptions>();
if (options.MyValue)
{
}
}
PreConfigure<TOptions>(this IServiceCollection, Action<TOptions>) and ExecutePreConfiguredActions<TOptions>(this IServiceCollection) are extension methods in Microsoft.Extensions.DependencyInjection (defined in Volo.Abp.Core; ExecutePreConfiguredActions<TOptions> requires TOptions : new()). ExecutePreConfiguredActions returns a fresh TOptions with every registered pre-config action applied in order.
Ordering that makes this work: across all modules, ABP runs every PreConfigureServices before any ConfigureServices. So the pre-config actions dependents register have all run by the time the producer calls ExecutePreConfiguredActions in its ConfigureServices. Multiple modules can pre-configure the same options and override each other's values based on their [DependsOn] order. A module exposes pre-config precisely so dependents can influence a decision the module makes at registration time, before the option is finalized.
Application startup (bootstrapping the ABP system)
Module classes describe what gets configured; something outside them has to create the ABP application container and initialize the modules. That's AbpApplicationFactory (namespace Volo.Abp).
Creating the container
AbpApplicationFactory.CreateAsync<TStartupModule>() (or Create<TStartupModule>() when you can't await) builds the container from a single root/startup module; every other module is pulled in as its [DependsOn] dependency. In a minimal console app you own the full lifecycle:
using var application = await AbpApplicationFactory.CreateAsync<MyConsoleDemoModule>();
await application.InitializeAsync();
await application.ShutdownAsync();
CreateAsync runs all modules' PreConfigureServices / ConfigureServices / PostConfigureServices; InitializeAsync() builds the service provider and runs the initialization lifecycle hooks. AbpApplicationFactory has overloads taking the module as a Type, and overloads taking an external IServiceCollection.
Internal vs external service provider
The overload distinction is real in the type system:
CreateAsync<TStartupModule>() (no IServiceCollection) returns Task<IAbpApplicationWithInternalServiceProvider> — ABP owns the IServiceCollection and builds the IServiceProvider itself. Its InitializeAsync() takes no argument. (The synchronous Create<TStartupModule>() returns the non-Task IAbpApplicationWithInternalServiceProvider.)
CreateAsync<TStartupModule>(IServiceCollection services) returns Task<IAbpApplicationWithExternalServiceProvider> — you (or a host framework) own DI. You build the provider (services.BuildServiceProviderFromFactory()) and hand it in: InitializeAsync(serviceProvider).
Hosted (framework-managed) vs manually-managed lifetime
In a console/standalone app you call InitializeAsync() / ShutdownAsync() yourself. In an ASP.NET Core (or .NET Generic Host) app the host manages the lifetime for you:
services.AddApplicationAsync<TStartupModule>(...) (extension in Microsoft.Extensions.DependencyInjection, returns Task<IAbpApplicationWithExternalServiceProvider>) registers the container against the host's IServiceCollection during service configuration.
app.InitializeApplicationAsync() (an IApplicationBuilder extension in the Microsoft.AspNetCore.Builder namespace, shipped by the Volo.Abp.AspNetCore package) initializes the modules and registers shutdown with IHostApplicationLifetime — ApplicationStopping triggers ShutdownAsync() and ApplicationStopped disposes the app. You don't call ShutdownAsync yourself; the host does. Templates generated by abp new wire this for you, so you rarely touch it directly.
AbpApplicationFactory returns an IAbpApplication, which is itself registered in DI and disposable — always dispose it before the process exits (the using var above, or the host's ApplicationStopped in the managed case).
Validation
- Derive from
AbpModule; add [DependsOn] for each package you install.
- Register services in
ConfigureServices; configure options with Configure<TOptions>.
- Use
Pre/PostConfigureServices for cross-module ordering.
- Prefer
ITransientDependency / IScopedDependency / ISingletonDependency over manual registration for your own services.
- The app boots and modules initialize in dependency order — verify a service registered in one module is resolvable from
context.ServiceProvider in a dependent module's OnApplicationInitialization.
Common Pitfalls
- Overriding only the async lifecycle method (e.g.
ConfigureServicesAsync) when the app may start on a synchronous path — the sync path skips your custom logic. Override both (or the sync version) unless you're certain of the startup path.
- Accessing
ServiceConfigurationContext outside Pre/Post/ConfigureServices — it is only available inside those methods and throws elsewhere.
- Declaring transitive dependencies in
[DependsOn] — declare only direct dependencies; ABP resolves the rest.
- Manually registering your own services when a lifetime interface (
ITransientDependency etc.) would do — prefer convention.