# Example: NCore service registration

`service-registration.md` records what a modern .NET (NCore) library adds to a
consuming application's dependency injection container: the registration
extension methods, every service they register with its lifetime, the options
they bind, the hosted services they start, and the services the host must still
register itself. Doc gen produces it at
`docs/libraries/<slug>/service-registration.md`, and only when the library
exposes at least one `IServiceCollection` / `IHostBuilder` /
`IApplicationBuilder` extension method, startup base class, middleware
extension, or hosted service.

## What reviewers should check

- Every public registration extension method in the library appears in the
  `## Registration extension catalog`, including generic helpers such as
  `AddMessageHandler<TMessage, THandler>`. A missing extension is a major
  finding.
- Every lifetime in `## Service registration catalog` is the one the source
  states (`AddSingleton`, `AddScoped`, `AddTransient`, `TryAddSingleton`,
  `AddHostedService`). A lifetime asserted from convention is a major finding.
- `TryAdd*` registrations say the host can override them, and a factory that
  returns `null` is named explicitly in the `Implementation` column.
- Conditional registrations carry the gating parameter, flag, or environment
  variable in the `Condition` column instead of saying "Always".
- `## Required-but-not-registered services` lists what the host must still
  register (here the environment client, a connection factory, and the message
  handlers themselves). The library's own registrations never appear there.
- Sections with no rows are omitted. This library has no startup base class or
  middleware pipeline, so those sections do not appear.
- Any marker `†` is followed by the canonical note box immediately before
  `## Sources`.
- No .NET Framework vocabulary appears: no `Web.config`, `AppSettings`,
  `IEnvironmentService`, or Autofac `ContainerBuilder`.
- `## Related documentation` links every standard page of the library except
  this one, including `configuration.md` because it exists.

## Example

The document below is a complete, correct `service-registration.md` for a
fictional library.

[View rendered example](rendered/libraries/saif-ncore-messaging/service-registration.md) — the same document as a real page, so you can compare rendering as well as source.

````markdown
---
title: Service registration
summary: Services, options, startup base classes, middleware, and hosted services this library contributes to a consuming application.
status: draft
owner: Messaging Platform Team
last_reviewed: 2026-10-21
library: SAIF.NCore.Messaging
reference_type: package
---

# Service registration

SAIF.NCore.Messaging exposes three `IServiceCollection` extension methods that
work in any hosting context: `AddSaifMessaging` registers the message bus
client, serializer, publisher, and handler dispatcher; `AddSaifMessagingOutbox`
adds the transactional outbox store and a background dispatcher for worker and
API hosts; and `AddMessageHandler<TMessage, THandler>` registers one message
handler. A consuming application must call `AddSaifMessaging` first, register
its own handlers, and supply an `IEnvironmentClient` and an
`IOutboxConnectionFactory` because the library resolves both but never
registers them.

## Registration extension catalog

Extension methods a consuming application calls to register this library's services.

| Extension method | Hosting context | Parameters | Source |
| --- | --- | --- | --- |
| `AddSaifMessaging(this IServiceCollection services, IConfiguration configuration)` | any | `configuration` is the application's root configuration; its `Messaging` section is bound to `MessagingOptions`. Must be called before `AddSaifMessagingOutbox` and `AddMessageHandler`. | src/SAIF.NCore.Messaging/Extensions/ServiceCollectionExtensions.cs |
| `AddSaifMessagingOutbox(this IServiceCollection services, IConfiguration configuration)` | API, worker | `configuration` is the application's root configuration; its `Messaging:Outbox` section is bound to `OutboxOptions`. Skips the hosted dispatcher when the `SAIF_MESSAGING_DISABLE_DISPATCH` environment variable is `true`. | src/SAIF.NCore.Messaging/Extensions/ServiceCollectionExtensions.cs |
| `AddMessageHandler<TMessage, THandler>(this IServiceCollection services)` | any | No parameters. `THandler` must implement `IMessageHandler<TMessage>`. Each call adds one handler; every handler registered for a message type is invoked for that type. | src/SAIF.NCore.Messaging/Extensions/MessageHandlerRegistrationExtensions.cs |

## Service registration catalog

| Service type | Implementation | Lifetime | Registered by | Condition | Source |
| --- | --- | --- | --- | --- | --- |
| `IMessageSerializer` | `JsonMessageSerializer` (from the bundled `SAIF.NCore.Messaging.Serialization` assembly) | Singleton | `AddSaifMessaging` | Always, through `TryAddSingleton`; a host registration made earlier wins | src/SAIF.NCore.Messaging/Extensions/ServiceCollectionExtensions.cs |
| `IMessageClock` | `SystemMessageClock` | Singleton | `AddSaifMessaging` | Always, through `TryAddSingleton`; a host registration made earlier wins | src/SAIF.NCore.Messaging/Extensions/ServiceCollectionExtensions.cs |
| `IMessageBusClient` | `ServiceBusClientAdapter` | Singleton | `AddSaifMessaging` | Always | src/SAIF.NCore.Messaging/Extensions/ServiceCollectionExtensions.cs |
| `IMessageAuditSink` | factory: `provider => null`, so the service resolves to `null` | Singleton | `AddSaifMessaging` | Always; `MessagePublisher` skips auditing when the resolved sink is `null` | src/SAIF.NCore.Messaging/Extensions/ServiceCollectionExtensions.cs |
| `IMessagePublisher` | `MessagePublisher` | Scoped | `AddSaifMessaging` | Always | src/SAIF.NCore.Messaging/Extensions/ServiceCollectionExtensions.cs |
| `IMessageHandlerDispatcher` | `MessageHandlerDispatcher` | Scoped | `AddSaifMessaging` | Always | src/SAIF.NCore.Messaging/Extensions/ServiceCollectionExtensions.cs |
| `IPostConfigureOptions<MessagingOptions>` | `ConfigureMessagingOptions` | Transient | `AddSaifMessaging` | Always, through `ConfigureOptions<ConfigureMessagingOptions>()` | src/SAIF.NCore.Messaging/Extensions/ServiceCollectionExtensions.cs |
| `IOutboxStore` | `SqlOutboxStore` | Scoped | `AddSaifMessagingOutbox` | Always | src/SAIF.NCore.Messaging/Extensions/ServiceCollectionExtensions.cs |
| `IHostedService` | `OutboxDispatchService` | Singleton | `AddSaifMessagingOutbox` | `SAIF_MESSAGING_DISABLE_DISPATCH` is not `true` | src/SAIF.NCore.Messaging/Extensions/ServiceCollectionExtensions.cs |
| `IMessageHandler<TMessage>` | `THandler` | Transient | `AddMessageHandler<TMessage, THandler>` | Once per call | src/SAIF.NCore.Messaging/Extensions/MessageHandlerRegistrationExtensions.cs |

## Options configuration

| Options type | Named instance | Configured by | Configuration keys / services read | Source |
| --- | --- | --- | --- | --- |
| `MessagingOptions` | default | `Configure<MessagingOptions>(configuration.GetSection("Messaging"))` in `AddSaifMessaging` | `Messaging:ServiceName`, `Messaging:DefaultTopic`, `Messaging:MaxDeliveryAttempts`, `Messaging:PublishTimeoutSeconds` | src/SAIF.NCore.Messaging/Options/MessagingOptions.cs |
| `MessagingOptions` | default | `ConfigureMessagingOptions` (post-configure, runs after binding) | `IEnvironmentClient`: `Messaging:TopicPrefix` (read only when `HasSetting` returns true) and the secure setting `Messaging:ConnectionString` | src/SAIF.NCore.Messaging/Options/ConfigureMessagingOptions.cs |
| `OutboxOptions` | default | `Configure<OutboxOptions>(configuration.GetSection("Messaging:Outbox"))` in `AddSaifMessagingOutbox` | `Messaging:Outbox:PollIntervalSeconds`, `Messaging:Outbox:BatchSize`, `Messaging:Outbox:RetentionHours` | src/SAIF.NCore.Messaging/Options/OutboxOptions.cs |

## Feature flags

Properties a consumer overrides, and environment variables a base class reads, that change registration or middleware behavior. Environment variables are also listed in [Configuration](configuration.md).

| Name | Kind | Default | Effect | Source |
| --- | --- | --- | --- | --- |
| `SAIF_MESSAGING_DISABLE_DISPATCH` | Environment variable (process) | Unset, treated as `false` | When `true`, `AddSaifMessagingOutbox` does not register `OutboxDispatchService`. Outbox rows are still written by `MessagePublisher` but this process never dispatches them. | src/SAIF.NCore.Messaging/Extensions/ServiceCollectionExtensions.cs |

## Hosted services and health checks

| Type | Kind | Registered by | Behavior | Source |
| --- | --- | --- | --- | --- |
| `OutboxDispatchService` | BackgroundService | `AddSaifMessagingOutbox` | Waits `Messaging:Outbox:PollIntervalSeconds` between cycles using a `PeriodicTimer`. Each cycle creates a service scope, reads up to `Messaging:Outbox:BatchSize` pending rows from `IOutboxStore`, sends them through `IMessageBusClient`, and marks them dispatched. A failed cycle is logged and the loop continues on the next tick. The stopping token is passed to the store and bus calls, so an in-flight batch is abandoned on shutdown and its rows stay pending. | src/SAIF.NCore.Messaging/Hosting/OutboxDispatchService.cs |

## Required-but-not-registered services

Services this library's types resolve that none of its registration methods add. The consuming application, or another library, must register them.

| Service type | Consumer | Expected source |
| --- | --- | --- |
| `IEnvironmentClient` | `ConfigureMessagingOptions` | Host application, through the environment client registration of `SAIF.NetCore.ServiceExtensions`† |
| `IOutboxConnectionFactory` | `SqlOutboxStore` | Host application; this library declares the interface and the host supplies the implementation |
| `IMessageHandler<TMessage>` | `MessageHandlerDispatcher` | Host application, through `AddMessageHandler<TMessage, THandler>` for each message type it consumes |
| `ILogger<T>` | `MessagePublisher`, `OutboxDispatchService` | Host application; added by the generic host or `AddLogging` |

## Related documentation
- Index
- Overview
- Architecture
- Dependencies
- [Configuration](configuration.md)
- Operations and processes
- Business rules
- API reference

> **Note:** Statements marked with `†` are derived from dependency library
> documentation via the Legends CLI. They describe inherited behavior from the
> indicated origin library and should be verified if the dependency version changes.

## Sources
- src/SAIF.NCore.Messaging/Extensions/ServiceCollectionExtensions.cs
- src/SAIF.NCore.Messaging/Extensions/MessageHandlerRegistrationExtensions.cs
- src/SAIF.NCore.Messaging/Options/MessagingOptions.cs
- src/SAIF.NCore.Messaging/Options/OutboxOptions.cs
- src/SAIF.NCore.Messaging/Options/ConfigureMessagingOptions.cs
- src/SAIF.NCore.Messaging/Hosting/OutboxDispatchService.cs
- src/SAIF.NCore.Messaging/Outbox/IOutboxConnectionFactory.cs
- src/SAIF.NCore.Messaging.Serialization/JsonMessageSerializer.cs
- src/SAIF.NCore.Messaging/SAIF.NCore.Messaging.csproj
````
