---
title: SAIF.NetCore.NHibernate architecture
summary: Architectural responsibilities, boundaries, and interaction patterns.
status: draft
owner: Framework
last_reviewed: 2026-10-02
library: SAIF.NetCore.NHibernate
reference_type: package
---

# SAIF.NetCore.NHibernate architecture

SAIF.NetCore.NHibernate is the repository's NHibernate data-access infrastructure provider: it defines a `IUnitOfWork`/`IUnitOfWorkFactory`/`IRepository<TEntity>`/`ISessionStrategy` stack, a named-options configuration model, and per-engine driver configurations for Oracle, SQL Server, and PostgreSQL, through FluentNHibernate — but it ships no entity mappings or business schema of its own, leaving that to consuming applications.

## Responsibilities
- Define the unit-of-work and repository abstractions (`IUnitOfWork`, `IUnitOfWorkFactory`, `IRepository<TEntity>`, `ISessionStrategy`, `ISessionHelper`) that consuming applications build entity access on top of.
- Build and cache an NHibernate `ISessionFactory` per connection string (`UnitOfWorkFactory`), selecting the Oracle, SQL Server, or PostgreSQL driver configuration from `UnitOfWorkOptions.Database`.
- Provide stateful and stateless session strategy implementations (`StatefulSessionStrategy`, `StatelessSessionStrategy`) with matching engine-specific `SessionHelper` implementations for a trivial system-date query.
- Supply a named-options configuration pattern (`UnitOfWorkOptions`, `ConfigureUnitOfWorkOptions`) through which a consuming application reads connection strings and environment settings via `IEnvironmentClient`/`IDBConnectionClient`.
- Register the modern DI surface (`AddNhibernateServices`) while retaining 13 legacy registration methods as `[Obsolete("Due to functional changes between .net core 2.2 and .net core 3.1, this method is obsolete to avoid startup errors.", true)]` compile-error stubs that force migration off the pre-.NET Core 3.1 API.

## Component boundaries
- `UnitOfWorkFactory` / `UnitOfWork`: lifecycle — builds/caches the `ISessionFactory` per connection string, begins/commits/rolls back the unit of work, and hands out `Repository<TEntity>` instances. `UnitOfWork`'s constructor is `internal`, so only `UnitOfWorkFactory` can create one.
- `Repository<TEntity>` (implements `IRepository<TEntity>`): the CRUD/query facade over a concrete `UnitOfWork`, exposing `Add`/`Update`/`Delete`/`Get`/`Load`/`Query()`.
- `ISessionStrategy` implementations (`StatefulSessionStrategy`, `StatelessSessionStrategy`): wrap NHibernate's `ISession`/`IStatelessSession`, the only point where consumers can reach the raw NHibernate session.
- `ISessionHelper` implementations (`OracleSessionHelper`, `SqlServerSessionHelper`, `PostgreSqlSessionHelper`): engine-specific system-date queries used by `SessionHelperBase.SystemDate`.
- `BaseConfiguration` and its engine subclasses (`OracleConfiguration`, `SqlConfiguration`, `PostGreSqlConfiguration`): FluentNHibernate driver/dialect configuration per `DatabaseType`, plus optional SQL debug output via `SqlDebugOutputInterceptor`.
- `UnitOfWorkOptions` / `ConfigureUnitOfWorkOptions` (abstract): the named-options POCO and the pattern a consuming application subclasses to resolve its connection string and engine from `IEnvironmentClient`/`IDBConnectionClient`.
- `IServicesCollectionExtensions` (`AddNhibernateServices`): the current DI registration surface. `IServicesNhibernateExtensions`: 13 legacy registration methods, each marked `[Obsolete]` with its error flag set to `true`, so calling any of them is a **compile-time error** — they are retained in the public API surface solely as compile-time-error migration diagnostics, not as working source-compatible registration methods, and must not be called.
- Contract boundary: `IEnvironmentClient` and `IDBConnectionClient` are declared in SAIF.NetCore.ServiceContracts; this library only consumes them as constructor-injected fields on a consumer-authored `ConfigureUnitOfWorkOptions` subclass.

## Interaction model
- A consuming application calls `AddNhibernateServices(assemblies, debugOutputSql: false)` (the non-generic overload requires `debugOutputSql`) or the generic `AddNhibernateServices<T>()` to register `IUnitOfWorkFactory` as a singleton and `UnitOfWorkAccessor` as a scoped service — nothing else.
- As a **separate** host registration, the consuming application must also register its own `ConfigureUnitOfWorkOptions` subclass, typically via `services.ConfigureOptions<TConfigurer>()`; `AddNhibernateServices` does not register this configurer or any `IConfigureOptions<UnitOfWorkOptions>` on the consumer's behalf.
- That subclass is constructed with `IEnvironmentClient` and `IDBConnectionClient` (both from SAIF.NetCore.ServiceContracts) and implements `GetConnectionString()` by calling the appropriate `IDBConnectionClient` method (`GetOracleConnection`/`GetSqlServerConnection`/`GetPostgreSqlServerConnection`) for its configured `DatabaseType`.
- At request time, the application resolves `IUnitOfWorkFactory` (directly, or through `UnitOfWorkAccessor.UnitOfWork(key)`) and calls `GetUnitOfWork(options)`; `UnitOfWorkFactory` builds (or reuses a cached) `ISessionFactory` keyed by connection string, selects the stateful or stateless session strategy, and returns an `IUnitOfWork`.
- The application then calls `GetRepository<TEntity>()` on the unit of work to obtain a `Repository<TEntity>` bound to its own `ClassMap<TEntity>` entity mappings — which this library does not supply — and issues `Add`/`Update`/`Delete`/`Get`/`Load`/`Query()` calls against it.
- An unrecognized `DatabaseType` value silently falls back to Oracle for both the driver configuration and the session helper*, rather than failing fast.
- No SAIF library in this repository consumes this package: `SAIF.NetCore.ServiceExtensions` does not reference it, even though the top-level architecture diagram draws a composition arrow from ServiceExtensions to the data group — applications must reference SAIF.NetCore.NHibernate directly*.

## Architecture diagram
The following diagram shows how a consuming application's unit-of-work options drive session-factory construction and entity access.

```mermaid
flowchart TD
    A["Consuming app:<br>ConfigureUnitOfWorkOptions subclass"] --> B["IEnvironmentClient /<br>IDBConnectionClient"]
    B --> C["UnitOfWorkOptions:<br>Database, ConnectionString"]
    C --> D[UnitOfWorkFactory.GetUnitOfWork]
    D --> E{"ISessionFactory cached<br>for connection string?"}
    E -->|no| F["BaseConfiguration subclass:<br>Oracle / Sql / PostGreSql driver"]
    F --> G["FluentNHibernate:<br>BuildSessionFactory"]
    E -->|yes| H[Reuse cached ISessionFactory]
    G --> I["SessionStrategy:<br>Stateful or Stateless"]
    H --> I
    I --> J["UnitOfWork"]
    J --> K["Repository&lt;TEntity&gt;:<br>consumer-supplied ClassMap"]
```

## Sequence diagram
The following diagram shows the primary request/response sequence for obtaining a unit of work and querying an entity.

```mermaid
sequenceDiagram
    participant App as "Consuming application"
    participant Factory as UnitOfWorkFactory
    participant Config as "ConfigureUnitOfWorkOptions"
    participant NH as "NHibernate / FluentNHibernate"
    participant Repo as "Repository#lt;TEntity#gt;"
    App->>Config: GetConnectionString() via IDBConnectionClient
    App->>Factory: GetUnitOfWork(options)
    Factory->>NH: BuildSessionFactory (cached by connection string)
    NH-->>Factory: ISessionFactory
    Factory-->>App: IUnitOfWork
    App->>Repo: unitOfWork.GetRepository<TEntity>()
    App->>Repo: Query() / Get(id) / Add(entity)
    Repo->>NH: Execute against ISession/IStatelessSession
    NH-->>Repo: Result
    Repo-->>App: Result
```

## Design constraints
- The library ships no entity mappings or business schema; the only concrete SQL it owns is three engine-specific `CreateSqlQuery` system-date statements and one generic LINQ call site (`UnitOfWork.TestConnection<TEntity>()`).
- `ISessionStrategy` exposes the raw NHibernate `ISession`/`IStatelessSession`, so consumers can bypass the repository abstraction entirely; it also declares `Dispose()` without implementing `IDisposable`.
- 13 legacy registration methods in `IServicesNhibernateExtensions` are `[Obsolete("Due to functional changes between .net core 2.2 and .net core 3.1, this method is obsolete to avoid startup errors.", true)]` no-ops; the checked-in `README.md` still documents this stale API and should not be treated as current.
- The static `SessionFactories` cache is keyed only by connection string, so two `ConfigureUnitOfWorkOptions` with the same connection string but different mapping assemblies would collide on cache lookup — unconfirmed in practice**.
- NHibernate's own resolved version is not pinned in this repository; it arrives transitively through FluentNHibernate 3.0.1**.

## Related documentation
- [Index](index.md)
- [Overview](overview.md)
- [Dependencies](dependencies.md)
- [Configuration](configuration.md)
- [Operations and processes](operations-processes.md)
- [Business rules](business-rules.md)
- [Database tables](database-tables.md)
- [SQL catalog](sql/index.md)
- [Service registration](service-registration.md)
- [API reference](api/index.md)
- [SAIF.NetCore.ServiceContracts overview](../saif-netcore-servicecontracts/overview.md)

> **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.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate.csproj`
- `src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/Factories/UnitOfWorkFactory.cs`
- `src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/Repository.cs`
- `src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/UnitOfWork.cs`
- `src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/Options/ConfigureUnitOfWorkOptions.cs`
- `src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/Extensions/IServicesCollectionExtensions.cs`
- `Architecture.md`
