SAIF.NetCore.ServiceExtensions architecture¶
SAIF.NetCore.ServiceExtensions is the top layer / composition root of the repository's three-layer model: it owns no domain logic of its own and instead provides IServiceCollection/IApplicationBuilder/IWebHostBuilder/HostBuilder/IHostApplicationBuilder extension methods, abstract ASP.NET Core startup base classes, console/Windows-service hosting bases, and OpenTelemetry composition helpers that wire together the foundation (SAIF.NetCore.ServiceContracts) and nine implementation-layer packages (ten internal package references in total) so that a consuming application only needs this single NuGet package to assemble a complete SAIF application.
Responsibilities¶
- Compose dependency injection registrations for the services implemented in other SAIF packages (auth, identity, DB connectivity, environment/configuration, error handling, HTTP clients, logging, OIDC, ping/health, system hashing) through 14 public
IServiceCollectionextension methods onIServiceCollectionExtensions. - Provide three ASP.NET Core startup base-class archetypes that encode the repository's standard application shapes: web UI with OIDC (
AppStartup→OidcMvcStartup/OidcRazorStartup), internal/Core API with dual Negotiate+JWT authentication (CoreAPIStartup), and external/Edge API with JWT-only authentication (EdgeAPIStartup). - Provide console host-builder configuration through
IHostBuilderExtensions.BuildConsoleApp<TConsole>/BuildSaifConsoleApp<TConsole>, which add configuration sources, logging, and a singletonIHostedServicedescriptor forTConsole— any class implementingIHostedService, not only the optionalConsoleBase/ConsoleAsyncBaseconvenience base classes this library also supplies. Separately, provideRegisterForWindowsService<T>/RegisterForWindowsService()asIServiceCollectionSUPPORT-service registrations (Windows event logger, Windows error handler, environment client, DB connection client) for a host/worker that the consuming application supplies and runs independently; these methods do not build or run a host and do not register the consumer'sIHostedService. - Supply cross-cutting middleware (
DynamicErrorHandler,DebugRequestHandler,LegacyBolAuthMiddleware,AspNetSessionIdMiddleware) and an MVCJwtControllerBase/ControllerBaseExtensionshelper surface for consistent HTTP error responses. - Compose optional OpenTelemetry logging/metrics/tracing (
OpenTelemetryExtensions.ConfigureOpenTelemetry) as an opt-in capability that no startup base class invokes automatically*.
Component boundaries¶
IServiceCollectionExtensions(SE/IServiceCollectionExtensions.cs): the primary composition surface — registersIEnvironmentClient/SecureEnvironmentClient(EnvironmentClient),IDBConnectionClient/ConnectionClient(DBConnectionClient), Edge/Core API auth services (AuthService), OIDC services (OIDC), the logging service (Logging), the error-handler pipeline (ErrorHandler, ServiceContractsIErrorHandleable), factory-activated middleware, and Windows-service registration (SystemHash for secure-setting hashing).IAppBuilderExtensions/IWebHostBuilderExtensions/IHostBuilderExtensions(SE/IAppBuilderExtensions.cs,IWebHostBuilderExtensions.cs,IHostBuilderExtensions.cs): pipeline- and host-builder-level composition — configuring the SAIF logger and error handling middleware, and configuring (not building or running) console hosts (BuildConsoleApp/BuildSaifConsoleAppreturn a configuredIHostBuilder; the consumer callsBuild()/Run()on it). Windows-service composition is a separate,IServiceCollection-only concern (RegisterForWindowsServiceinIServiceCollectionExtensions), not a host-builder responsibility.OpenTelemetryExtensions/OpenTelemetryOptions(SE/OpenTelemetryExtensions.cs,OpenTelemetryOptions.cs): a self-contained observability composition boundary built directly on theOpenTelemetry.*package family, independent of the other SAIF package registrations.Startupnamespace (SE/Startup/*.cs):AppStartup(abstract web base, host-location-driven auth branching),OidcMvcStartup/OidcRazorStartup(web UI archetypes),BaseApiStartup(shared API base),CoreAPIStartup(internal API archetype),EdgeAPIStartup(external API archetype) — these classes are the boundary between this library's composition logic and an application's ownStartup/Programtype, which derives from one of them.Middlewarenamespace (SE/Middleware/*.cs): fourIMiddlewareimplementations that this library both defines and registers (DynamicErrorHandler,DebugRequestHandler,LegacyBolAuthMiddleware,AspNetSessionIdMiddleware);LegacyBolAuthMiddlewareis defined here, not inSAIF.NetCore.LegacyAuth.ControllerBaseExtensions/JwtControllerBase/IConfigurationExtensions/IWindowsServiceErrorHandlerExtensions: smaller, self-contained helper boundaries (HTTP error responses, JWT claim reads, configuration key validation, Windows-service error-handler fluent setup) that do not depend on the DI registration surface.
Interaction model¶
- Composition happens at application startup, not at request time: a consuming application's
Program/StartupcallsIServiceCollectionExtensionsmethods (e.g.RegisterEdgeAPIAuthServices,RegisterOidcServices,RegisterDBConnectionClient) duringConfigureServices/host building, which registers the concrete implementation types owned by AuthService, DBConnectionClient, EnvironmentClient, OIDC, SystemHash, and the other composed packages into theIServiceCollection. - The three startup archetypes (
AppStartup/CoreAPIStartup/EdgeAPIStartup) each call a fixed sequence of these registration and pipeline-configuration extension methods in theirConfigureServices/Configureoverrides, so an application derives from one archetype and overrides onlyConfigureUI/ConfigureMvcBuilderrather than re-assembling the DI graph itself. AppStartupbranches its entire authentication composition on theASPNETCORE_HOST_LOCATIONenvironment variable at startup time:EXTERNALselects OIDC cookie/OpenIdConnect authentication plus SecurityApi authorization, any other value selects Core API dual Negotiate/JWT authentication*.IMiddlewareinstances are factory-activated, soIServiceCollectionExtensions/AppStartupmust register them in DI beforeUseMiddleware<T>()adds them to the pipeline inIAppBuilderExtensions/Configure;DynamicErrorHandleraggregates all registeredIErrorHandleableimplementations (ServiceContracts) and delegates to the single registeredIErrorHandlerplus anyIErrorHandlerHookimplementations.- OpenTelemetry composition is independent of the startup archetypes: an application calls
ConfigureOpenTelemetryitself (onIHostBuilderorIHostApplicationBuilder) if it wants OTel logging/metrics/tracing; enabling it replaces the application's existing logging providers with OTel's own*. - Console applications bypass the ASP.NET Core startup archetypes entirely: a consumer supplies any
IHostedService(optionally one of this library'sConsoleBase/ConsoleAsyncBaseconvenience bases) toIHostBuilderExtensions.BuildConsoleApp<TConsole>/BuildSaifConsoleApp<TConsole>. Both methods return a configuredIHostBuilder—BuildConsoleAppadds configuration sources, a singletonIHostedService → TConsoledescriptor, and console logging viaConfigureAppConfiguration/ConfigureServices/ConfigureLogging;BuildSaifConsoleAppadditionally chainsRegisterSecureEnvironmentClient,RegisterDBConnectionClient, andIConsoleLogServiceregistrations onto that same builder. Neither method callsBuild()orRun(): the consumer's ownProgramentry point calls.Build().Run()(or.RunAsync()) on the returnedIHostBuilderitself, and that is whenTConsole'sIHostedServiceis actually constructed and started. Windows services are a distinct pattern: the consumer builds and runs its own host/worker (for example aBackgroundService) independently, and callsRegisterForWindowsService<T>/RegisterForWindowsService()only to add Windows-specific SUPPORT services (Windows event logger, Windows error handler, environment client, DB connection client) to that host'sIServiceCollection— this call neither builds a host nor registers the consumer'sIHostedService, and does not require deriving from either console base class.
Architecture diagram¶
The following diagram shows ServiceExtensions as the top-layer composition root sitting above the foundation and implementation-layer packages, composing them for the four application archetypes it supports.
flowchart TB
subgraph Foundation["Foundation layer"]
SC["SAIF.NetCore.ServiceContracts"]
end
subgraph Implementation["Implementation-layer packages composed by this library"]
direction LR
AUTH["AuthService"]
DB["DBConnectionClient"]
ENV["EnvironmentClient"]
ERR["ErrorHandler"]
HTTP["HttpClients"]
LOG["Logging"]
OIDC["OIDC"]
PING["PingableService"]
HASH["SystemHash"]
end
SE["SAIF.NetCore.ServiceExtensions<br>(composition root: IServiceCollectionExtensions,<br>startup bases, middleware, OTel)"]
SE -.->|NuGet ref| SC
SE -.->|NuGet ref| AUTH
SE -.->|NuGet ref| DB
SE -.->|NuGet ref| ENV
SE -.->|NuGet ref| ERR
SE -.->|NuGet ref| HTTP
SE -.->|NuGet ref| LOG
SE -.->|NuGet ref| OIDC
SE -.->|NuGet ref| PING
SE -.->|NuGet ref| HASH
subgraph Apps["Application archetypes (consumer-derived)"]
direction LR
WEB["AppStartup →<br>OidcMvcStartup / OidcRazorStartup"]
CORE["CoreAPIStartup<br>(dual Negotiate+JWT)"]
EDGE["EdgeAPIStartup<br>(JWT only)"]
CONSOLE["Console host<br>(consumer calls Build/Run; any IHostedService,<br>optionally ConsoleBase / ConsoleAsyncBase)"]
WINSVC["Windows service host/worker<br>(consumer-supplied, independent)"]
end
WEB -->|derives from| SE
CORE -->|derives from| SE
EDGE -->|derives from| SE
CONSOLE -->|configured via BuildConsoleApp /<br>BuildSaifConsoleApp — consumer builds/runs| SE
WINSVC -->|adds support services via<br>RegisterForWindowsService| SE
OTEL["OpenTelemetry.* packages<br>(opt-in, independent)"] --> SE
Sequence diagram¶
The following diagram shows the startup-time composition sequence when an application derives from EdgeAPIStartup, split into its two distinct phases: ConfigureServices (DI registration, no HTTP pipeline yet) and Configure (HTTP request pipeline construction). This is a startup-time composition sequence, not a runtime request call chain.
sequenceDiagram
participant App as Application Program/Startup
participant Edge as EdgeAPIStartup
participant ISCE as IServiceCollectionExtensions
participant DI as IServiceCollection
participant Pipe as IApplicationBuilder pipeline
Note over App,DI: Phase 1 — ConfigureServices (registration only)
App->>Edge: derive from EdgeAPIStartup
Edge->>ISCE: RegisterEnvironmentClient()
ISCE->>DI: register non-secure IEnvironmentClient + null ISystemHashService
Edge->>DI: AddControllers() / ConfigureBuilder()
Edge->>ISCE: RegisterLoggingService()
ISCE->>DI: register ISaifLogService
Edge->>ISCE: RegisterEdgeAPIAuthServices()
ISCE->>DI: register AuthService JWT-only auth types
Edge->>DI: RegisterFactoryActivatedMiddleware()
Note over App,Pipe: Phase 2 — Configure (HTTP pipeline construction)
Edge->>Pipe: ConfigureMiddleware(app): buffering, error handler, debug handler (before auth)
Edge->>Pipe: UseAuthentication / UseHsts / UseHttpsRedirection / UseRouting / UseAuthorization
Edge->>Pipe: UseEndpoints(MapControllers)
Edge->>Pipe: ConfigureSaifLogger()
Pipe-->>App: fully composed request pipeline
Design constraints¶
- No project references between libraries in this repository; every cross-library dependency, including all ten this library composes, is a NuGet package reference, consistent with the repository-wide layering in
Architecture.md. - This library directly references only 10 of the other 18 libraries in this repository (AuthService, DBConnectionClient, EnvironmentClient, ErrorHandler, HttpClients, Logging, OIDC, PingableService, ServiceContracts, SystemHash);
HashUtil,Common,Clients.UC4,LegacyAuth,NHibernate,NtlmApiAuth,Services.Messaging, andWindowsServiceare not referenced and are not wired by this library, soArchitecture.md's description of ServiceExtensions pulling in "the full set" of SAIF services is only partially accurate*. AppStartup's authentication branch is driven by a machine/process environment variable (ASPNETCORE_HOST_LOCATION) read at startup, making the composed authentication stack an environment-configuration concern rather than a compile-time one*.- Enabling
ConfigureOpenTelemetryreplaces all existing logging providers with OTel's own, so console/other logging configured before the call is lost unless re-added by the application*. - Several Windows-specific assumptions are present:
EnvironmentVariableTarget.Machineenvironment-variable reads, the Negotiate/IIS authentication scheme, and theAPP_POOL_IDenvironment variable used for theiis.app_pool.nameOpenTelemetry resource attribute are all silently inert on non-Windows hosts*.
Related documentation¶
- Index
- Overview
- Dependencies
- Configuration
- Operations and processes
- Business rules
- Security and authorization
- Service registration
- API reference
- SAIF.NetCore.ServiceContracts overview
- SAIF.NetCore.AuthService overview
- SAIF.NetCore.OIDC overview
Note: Statements marked with
*were inferred from code structure, naming conventions, or contextual evidence where direct confirmation was not available in source artifacts. Statements marked with**could not be confirmed; treat as unknown until verified.
Sources¶
src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions.csprojsrc/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/IServiceCollectionExtensions.cs,IAppBuilderExtensions.cs,IWebHostBuilderExtensions.cs,IHostBuilderExtensions.cssrc/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/OpenTelemetryExtensions.cs,OpenTelemetryOptions.cssrc/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/IConfigurationExtensions.cs,ControllerBaseExtensions.cs,IWindowsServiceErrorHandlerExtensions.cs,ConsoleAsyncBase.cs,ConsoleBase.cs,JwtControllerBase.cs,Enums.cssrc/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/Middleware/DynamicErrorHandler.cs,DebugRequestHandler.cs,LegacyBolAuthMiddleware.cs,AspNetSessionIdMiddleware.cssrc/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/Startup/AppStartup.cs,OidcMvcStartup.cs,OidcRazorStartup.cs,BaseApiStartup.cs,CoreAPIStartup.cs,EdgeAPIStartup.csArchitecture.md