BaseTemplate DI Extensibility¶
Overview¶
ConnectSoft.BaseTemplate provides a default DI and middleware orchestration pipeline that can be reused and specialized by higher-layer templates (for example Identity).
The extensibility model is based on:
MicroserviceRegistrationExtensionsas entry pointsDefaultMicroserviceRegistrationfor unchanged default behaviorMicroserviceRegistrationBaseprotected virtual hooks for targeted customization
This approach reduces duplicated startup/DI orchestration code across repositories while keeping extension points explicit.
Compile-time vs runtime: DI hooks control runtime service registration. MSBuild flags in Directory.Build.props (and optional DisableMicrosoftExtensionsStackForMinimalHost.props in extended repos) control which optional stacks and ConnectSoft.Extensions.* consumers are active at build time. See Template layering and reuse — MSBuild layering and ConnectSoft.Extensions catalog.
Terminology used in this guide:
- base + add
- replace whole step
- default-preserving hooks
- submodule refresh
Architecture Contract¶
At startup, registration calls delegate to a registration class instead of hardcoding all logic in static extension methods:
ConfigureMicroserviceServices(...)UseMicroserviceServices(...)
Default flow remains in BaseTemplate through DefaultMicroserviceRegistration. Derived templates extend by inheriting from MicroserviceRegistrationBase.
Hook Usage Patterns¶
Additive (base + add)¶
Use this when BaseTemplate behavior should stay intact and template-specific registrations are appended.
Typical examples:
- additional localization resources
- named/typed HTTP clients
- template-specific metrics
Full Step Replacement (replace whole step)¶
Use this when a template must alter the full registration step.
Typical examples:
- health checks subset/scope
- problem details strategy
- migrations assembly selection
Common Hook Areas¶
MicroserviceRegistrationBase organizes hooks by stage, including:
- domain model registration stages
- localization, HTTP clients, and metrics
- service discovery, redaction, rate limiting, and web security
- API stack (problem details, REST infrastructure, Swagger, gRPC)
- persistence and migrations (
GetMigrationAssembly, migrator registration) - health checks and validation
- endpoint mapping and shutdown stages
DI Hook Reference¶
The table below lists the most commonly overridden hooks from MicroserviceRegistrationBase. All web-host extended templates (Identity, Auth Server, Microservice, Bot Framework, Health Checks Aggregator, API Gateway) follow the three-layer DI chain ApplicationModelRegistrationBase → MicroserviceRegistrationBase → template-specific registration. The Worker template uses a simpler pattern based on the generic host and does not inherit from MicroserviceRegistrationBase.
| Hook | Responsibility | When to Override |
|---|---|---|
RegisterOptions |
Binds IOptions<T> / IConfigureOptions<T> from configuration sections. Base registers shared infrastructure options from the "BaseTemplate" config section. |
Override with replace strategy (do NOT call base). Each extended template has its own root options section name (e.g., "Microservice", "ApiGateway") and its own OptionsExtensions.AddMicroserviceOptions() that registers all infrastructure options plus template-specific bindings. |
RegisterDomainModelServices |
Registers domain-layer services such as repositories, domain services, and aggregate factories via the Application Model scanning conventions. | Override when the template introduces its own domain model assembly or needs to register domain services that the base scanning does not cover. |
ConfigureServicesAfterDomainModel |
Runs after domain model registration completes. Intended for services that depend on domain registrations being present in the container. | Override to wire up cross-cutting concerns that depend on domain types — for example, decorators, domain event dispatchers, or saga orchestrators. |
RegisterTemplateMetrics |
Registers OpenTelemetry Meter instances and custom metric instruments for the template. Base registers shared infrastructure meters. |
Override to add template-specific meters and counters (e.g., IdentityAuthenticationMetrics, GatewayRoutingMetrics). |
MapMicroserviceEndpoints |
Maps HTTP endpoints (minimal APIs, controllers, gRPC services) onto the request pipeline after middleware is configured. | Override to map template-specific endpoints. Call base first to keep shared health-check and diagnostics endpoints, then add domain routes. |
UseSecurityMiddleware |
Configures authentication, authorization, CORS, and rate-limiting middleware in the request pipeline. | Override when the template requires a different auth scheme (e.g., Auth Server's OpenIddict validation pipeline) or needs to alter CORS / rate-limiting policies. Always call base unless fully replacing the security stack. |
ConfigureAdditionalServices |
Catch-all hook that runs at the end of the service-registration phase. Receives the full IServiceCollection. |
Override for any registration that does not fit the earlier, more specific hooks — for example, hosted background services, named HttpClient registrations, or third-party SDK setup. |
Extended Template Registration Classes¶
Each extended template provides a single registration coordinator class that inherits from MicroserviceRegistrationBase and overrides only the hooks where template-specific behavior is needed. All other DI orchestration is inherited from the base.
| Template | Registration Class | Key Overrides |
|---|---|---|
| Identity | IdentityMicroserviceRegistration |
RegisterOptions, RegisterAutoMapper, RegisterPersistenceModel, RegisterFluentValidation, RegisterGrpcInfrastructure, RegisterHealthChecks, RegisterHostedServices, RegisterOpenTelemetry, RegisterHttpClients, RegisterMessaging, RegisterProblemDetails, RegisterWebApiInfrastructure, RegisterSwagger, RegisterDomainModelServices, ConfigureServicesAfterDomainModel, RegisterTemplateMetrics, MapMicroserviceEndpoints |
| Auth Server | AuthServerMicroserviceRegistration |
RegisterOptions, RegisterAutoMapper, RegisterPersistenceModel, RegisterFluentValidation, RegisterHealthChecks, RegisterHostedServices, RegisterOpenTelemetry, RegisterHttpClients, RegisterMessaging, RegisterProblemDetails, RegisterWebApiInfrastructure, RegisterSwagger, RegisterDomainModelServices, ConfigureServicesAfterDomainModel, RegisterTemplateMetrics, MapMicroserviceEndpoints |
| Bot Framework | BotFrameworkMicroserviceRegistration |
RegisterOptions, RegisterAutoMapper, RegisterPersistenceModel, RegisterFluentValidation, RegisterGrpcInfrastructure, RegisterHealthChecks, RegisterHostedServices, RegisterOpenTelemetry, RegisterHttpClients, RegisterMessaging, RegisterProblemDetails, RegisterWebApiInfrastructure, RegisterSwagger, RegisterDomainModelServices, ConfigureServicesAfterDomainModel, RegisterTemplateMetrics, MapMicroserviceEndpoints |
| API Gateway | ApiGatewayMicroserviceRegistration |
RegisterOptions, RegisterAutoMapper, RegisterFluentValidation, RegisterHealthChecks, RegisterHostedServices, RegisterOpenTelemetry, RegisterHttpClients, RegisterRedaction, RegisterProblemDetails, RegisterWebApiInfrastructure, RegisterSwagger, RegisterDomainModelServices, RegisterTemplateMetrics, MapMicroserviceEndpoints |
| Health Checks Aggregator | HealthChecksAggregatorRegistration |
RegisterOptions, RegisterHealthChecks, RegisterHostedServices, RegisterOpenTelemetry, RegisterHttpClients, RegisterProblemDetails, RegisterWebApiInfrastructure, RegisterDomainModelServices, RegisterTemplateMetrics, MapMicroserviceEndpoints |
| Microservice Template | MicroserviceTemplateRegistration |
RegisterOptions, RegisterAutoMapper, RegisterPersistenceModel, RegisterFluentValidation, RegisterGrpcInfrastructure, RegisterHealthChecks, RegisterHostedServices, RegisterOpenTelemetry, RegisterHttpClients, RegisterMessaging, RegisterProblemDetails, RegisterWebApiInfrastructure, RegisterSwagger, RegisterDomainModelServices, ConfigureServicesAfterDomainModel, RegisterTemplateMetrics, MapMicroserviceEndpoints |
Override Strategy¶
Each override typically follows one of two strategies:
- Replace: The override directly calls the template's own static helper method (e.g.,
AutoMapperExtensions.AddMicroserviceAutoMapper(services)) without callingbase. Used when the template's method fully covers all needed registrations and calling both would produce duplicates. - Base + Add: The override calls
base.RegisterXxx(...)first to retain shared infrastructure registrations, then appends template-specific registrations. Used forRegisterOptionsandMapMicroserviceEndpointsin most templates.
Duplicate Extension Cleanup¶
During alignment, infrastructure-only extension files that were exact duplicates of base template functionality were identified and removed from extended templates. These included:
AzureAppConfigurationExtensions,AzureApplicationInsightsExtensions,CompressionExtensionsFeatureFlagsExtensions,HttpLoggingExtensions,MicroserviceRedactionExtensionsLatencyTelemetryCollectionExtensions,RateLimitingExtensions,RequestTimeoutExtensionsResourceMonitoringExtensions,SerilogLoggingExtensions,ServiceDiscoveryExtensionsHeaderPropagationExtensions,WebSecurityExtensions,LocalizationExtensions
These registrations are now handled by MicroserviceRegistrationBase hooks in the base template.
OpenTelemetryAttributeName Consolidation¶
OpenTelemetryAttributeName.cs was removed from all extended templates. The type is sourced from the ConnectSoft.Extensions.Observability NuGet package. Extended templates reference it via using ConnectSoft.Extensions.Observability; in their OpenTelemetryExtensions.cs files.
Program.cs Serilog Bootstrap Alignment¶
All extended templates use the modern Serilog bootstrap pattern from ConnectSoft.Extensions.Logging.Serilog:
#if Serilog
Log.Logger = SerilogBootstrapExtensions.CreateBootstrapLogger();
try
{
#endif
// ... host setup ...
#if Serilog
}
catch (Exception exception)
{
Log.Fatal(exception, "Application failed to start");
throw;
}
finally
{
await Log.CloseAndFlushAsync().ConfigureAwait(false);
}
#endif
The host builder uses .UseSerilogBootstrap() instead of .UseSerilog(...) to leverage the bootstrap logger upgrade pattern. All Logger.Information(...) calls use the static Log.Information(...) API from Serilog.
Azure App Configuration Null-Guard¶
All Program.cs and test BeforeAfterTestRunHooks.cs files include a null-guard before options.Connect():
string? azureAppConfigurationConnectionString = configurationRoot.GetConnectionString("AzureAppConfiguration");
if (string.IsNullOrWhiteSpace(azureAppConfigurationConnectionString))
{
throw new InvalidOperationException(
"Azure App Configuration is enabled but the connection string is missing. " +
"Set `ConnectionStrings:AzureAppConfiguration` in configuration.");
}
options.Connect(azureAppConfigurationConnectionString)
Migration Guide (from duplicated DI code)¶
- Keep BaseTemplate defaults as the source of truth.
- Identify duplicated DI orchestration in derived template repos.
- Move template-specific behavior into
MicroserviceRegistrationBaseoverrides. - Remove mostly identical orchestration code from derived templates.
- Remove local copies of types available from shared NuGet packages (e.g.,
OpenTelemetryAttributeNamefromConnectSoft.Extensions.Observability). - Align
Program.csto useSerilogBootstrapExtensions.CreateBootstrapLogger()with#if Serilogguards and.UseSerilogBootstrap(). - Add Azure App Configuration null-guard in both
Program.csand testBeforeAfterTestRunHooks.cs. - Add
HealthCheckPublisherOptionsspeed-up (1s delay, 2s period) inTestStartup.ConfigureServices. - Respect submodule boundaries: refresh submodule to consume BaseTemplate changes; do not edit submodule contents directly.
- Validate by building and running startup smoke checks (endpoints, health checks, and middleware behavior).