---
title: SAIF.Framework.Data.NHibernate business rules
summary: Enumerated business rules with per-rule trigger, enforcement, and outcomes.
status: draft
owner: Framework
last_reviewed: 2026-09-15
library: SAIF.Framework.Data.NHibernate
reference_type: package
---

# SAIF.Framework.Data.NHibernate business rules

This library is infrastructure/data-access code; it enforces a small number of structural invariants rather than domain business rules. No `IsValid()`/`AddErrorMessage()` domain validation was found — this is not a BOF-style business object library.

## BR-001 Transaction already in progress

| Field | Value |
| --- | --- |
| **Statement** | A unit of work cannot begin a new transaction while one is already active (active, committed, or rolled back state). |
| **Trigger** | `NHibernateUnitOfWork.Begin()` called on a unit of work with an in-progress transaction. |
| **Enforcement** | `UnitOfWork/NHibernateUnitOfWork.cs::Begin()` / `IsTransactionInProgress()` |
| **Outcome** | Throws `InvalidOperationException("There is a transaction already in progress.")`. |
| **Source** | `UnitOfWork/NHibernateUnitOfWork.cs` |

---

## BR-002 Commit requires a prior Begin

| Field | Value |
| --- | --- |
| **Statement** | A unit of work cannot be committed unless `Begin()` was called first (transaction must be active). |
| **Trigger** | `NHibernateUnitOfWork.Commit()` called without a preceding `Begin()`. |
| **Enforcement** | `UnitOfWork/NHibernateUnitOfWork.cs::Commit()` |
| **Outcome** | Throws `InvalidOperationException("You must call Begin() on the unit of work before you can commit.")`. |
| **Source** | `UnitOfWork/NHibernateUnitOfWork.cs` |

---

## BR-003 Stateless updates require explicit persistence

| Field | Value |
| --- | --- |
| **Statement** | Under the stateless session strategy, entity updates are never implicit — every change must be explicitly persisted via `Update()`; there is no automatic dirty-checking/flush-on-commit as there is for the stateful strategy. |
| **Trigger** | Any call to `RepositoryBase<TEntity>.Update(entity)` when the underlying unit of work uses `StatelessSessionStrategy` (i.e., obtained via `OracleStatelessUnitOfWorkFactory`). |
| **Enforcement** | `Sessions/StatelessSessionStrategy.cs::Update()` (calls `session.Update(entity)`) vs. `Sessions/StatefulSessionStrategy.cs::Update()` (empty no-op body) |
| **Outcome** | Stateless: entity change is written immediately; Stateful: no separate write occurs — reliance on NHibernate's automatic dirty-checking at `Commit()`. |
| **Source** | `Sessions/StatelessSessionStrategy.cs`, `Sessions/StatefulSessionStrategy.cs` |

---

## BR-004 SystemDate falls back to local clock

| Field | Value |
| --- | --- |
| **Statement** | `SystemDate` (server-side "now") is derived from a live database query, but falls back to local `DateTime.Now` if the query returns null, `DateTime.MinValue`, or `DateTime.MaxValue`. |
| **Trigger** | Any read of `RepositoryBase<TEntity>.SystemDate` / `ISessionHelper.SystemDate`. |
| **Enforcement** | `SessionHelper/SessionHelperBase.cs::SystemDate` (`if (!result.IsMinOrMax()) return result; return DateTime.Now;`) |
| **Outcome** | Callers relying on `SystemDate` for audit timestamps may silently get local server time instead of database time if the SQL round-trip fails to return a sane value — this is a documented fallback behavior, not a validated business rule, but it materially affects timestamp provenance. |
| **SQL** | [SQL-004](sql/SQL-004-saif-framework-data-nhibernate.md) |
| **Source** | `SessionHelper/SessionHelperBase.cs` |

---

## BR-005 Repository is scoped to one unit of work

| Field | Value |
| --- | --- |
| **Statement** | A `Repository<TEntity>` is scoped to exactly one `NHibernateUnitOfWork` instance's session for its entire lifetime — repositories are not reusable across units of work. |
| **Trigger** | `NHibernateUnitOfWork.GetRepository<TEntity>()` always constructs a new `Repository<TEntity>` bound to `this`. |
| **Enforcement** | `UnitOfWork/NHibernateUnitOfWork.cs::GetRepository<TEntity>()`, `Repositories/RepositoryBase.cs` constructor |
| **Outcome** | All repositories obtained from the same unit of work instance participate in the same transaction (the NHibernate-stack analogue of BOF's `AppManager.Write()` transaction wrapping). |
| **Source** | `UnitOfWork/NHibernateUnitOfWork.cs`, `Repositories/RepositoryBase.cs` |

---

## Notes
- No rule in this library originates from an inherited BOF/`clsBusinessBase` base class — no such inheritance was found.
- [BR-001](#br-001-transaction-already-in-progress)/[BR-002](#br-002-commit-requires-a-prior-begin)/[BR-005](#br-005-repository-is-scoped-to-one-unit-of-work) govern the unit-of-work transaction lifecycle and repository scoping; [BR-003](#br-003-stateless-updates-require-explicit-persistence) governs the stateful/stateless divergence; [BR-004](#br-004-systemdate-falls-back-to-local-clock) governs the `SystemDate` fallback behavior.

## Related documentation
- [Index](index.md)
- [Overview](overview.md)
- [Architecture](architecture.md)
- [Dependencies](dependencies.md)
- [Operations and processes](operations-processes.md)
- [Database tables](database-tables.md)
- [SQL catalog](sql/index.md)
- [API reference](api/index.md)

## Sources
- `UnitOfWork/NHibernateUnitOfWork.cs`
- `Sessions/StatefulSessionStrategy.cs`
- `Sessions/StatelessSessionStrategy.cs`
- `SessionHelper/SessionHelperBase.cs`
- `Repositories/RepositoryBase.cs`
