---
title: SAIF.Application business rules
summary: Enumerated business rules enforced by the SAIF.Application library.
status: draft
owner: SAIF Doc Gen
last_reviewed: 2026-07-30
library: SAIF.Application
reference_type: package
---

# SAIF.Application business rules

This page captures the library-level operational rules enforced by `SAIF.Application`.

## BR-001: Single effective application security role

| Field | Value |
| --- | --- |
| Statement | An application manager instance exposes exactly one effective security role at a time: `ViewOnly`, `Update`, or `NoAccess`. |
| Trigger | A consuming implementation assigns application access through `MarkUpdate()`, `MarkViewOnly()`, or `MarkNoAccess()`. |
| Enforcement | `AppManagerBase` stores a single `m_AppSecurityRole` value and derives `SecurityRole`, `IsAccessible`, `IsUpdateable`, `IsViewOnly`, and `IsRestricted` from that value. |
| Outcome | Callers receive a mutually exclusive role state for the current manager instance. |
| Confidence | Verified |
| Source | `Application\SAIF.Application\AppManagerBase.vb` |

---

## BR-002: Duplicate message text is suppressed

| Field | Value |
| --- | --- |
| Statement | Duplicate message text is not appended more than once within the active message aggregation result. |
| Trigger | `AppendMessageLines(...)` or `MessageFormatting` helper methods process message content. |
| Enforcement | `AppManagerBase` and `MessageFormatting` test whether a candidate line is already present before appending it. |
| Outcome | The resulting message string avoids repeated identical message lines. |
| Confidence | Verified |
| Source | `Application\SAIF.Application\AppManagerBase.vb`; `Application\SAIF.Application\MessageFormatting.vb` |

---

## BR-003: Authorization results are cached per resource name

| Field | Value |
| --- | --- |
| Statement | Authorization outcomes are cached by resource name for the lifetime of a `UserAccess` instance. |
| Trigger | Calling code invokes `HasAccess(resourceName)` for a resource name that is not already cached. |
| Enforcement | `UserAccess` stores the `clsSecurityPrincipal.IsAuthorized(...)` result in a lower-cased resource-name cache and reuses that stored value on later checks. |
| Outcome | Repeat checks for the same resource name do not require another authorization call through the security principal. |
| Confidence | Verified |
| Source | `Application\SAIF.Application\UserAccess.vb` |

---

## BR-004: Data portal keys are registered case-insensitively and only once

| Field | Value |
| --- | --- |
| Statement | A keyed `IDataPortalInfo` entry is stored under the lower-cased form of its key, and a key that is already present is never overwritten by a later registration or injection. |
| Trigger | `ApplicationDataAccess.CreateSQLServerPortal(key)`, `CreateOraclePortal(key)`, or `AppManagerBase.InjectDPI(key, dpi)` sets the `DPI(key)` property for a key. |
| Enforcement | The `DPI` property setter lower-cases the key and only calls `m_DPILibrary.Add(...)` when `ContainsKey` is false for that lower-cased key; the getter also lower-cases the key before lookup. |
| Outcome | Callers can register or inject a portal under a given key at most once per `ApplicationDataAccess` instance; subsequent attempts with the same key (in any casing) are silently ignored rather than replacing the existing entry. |
| Confidence | Verified |
| Source | `Application\SAIF.Application\ApplicationDataAccess.vb` |

---

## BR-005: Batch ID overrides the data-access client ID for portal environments

| Field | Value |
| --- | --- |
| Statement | When a non-blank `BatchID` is set on `ApplicationDataAccess`, the environment's `ClientID` used to build a new SQL Server or Oracle portal is the `BatchID` value instead of the underlying `clsDataAccess.ClientID`. |
| Trigger | `CreateSQLServerPortal(key)` or `CreateOraclePortal(key)` is called on an `ApplicationDataAccess` instance whose `BatchID` property has been set to a non-blank value. |
| Enforcement | Both portal-creation methods first set `m_Env.ClientID = m_DAL.ClientID`, then overwrite it with `m_Env.ClientID = BatchID` when `BatchID.Trim.Length > 0`, before constructing the `clsSQLDataPortal` or `clsMSOracleDataPortal`. |
| Outcome | Batch-hosted callers (constructed via `New ApplicationDataAccess(inBatchUserID)` or with `BatchID` set explicitly) get portals whose environment identifies the batch user rather than the ambient data-access client. |
| Confidence | Verified |
| Source | `Application\SAIF.Application\ApplicationDataAccess.vb` |

## Notes
- These rules describe library infrastructure behavior rather than claim-domain validations or database rules.

## Related documentation
- [Index](index.md)
- [Overview](overview.md)
- [Architecture](architecture.md)
- [Dependencies](dependencies.md)
- [Operations and processes](operations-processes.md)
- [Security and authorization](security-authorization.md)
- [API reference](api/index.md)
- [Configuration](configuration.md)

## Sources
- `Application\SAIF.Application\AppManagerBase.vb`
- `Application\SAIF.Application\MessageFormatting.vb`
- `Application\SAIF.Application\UserAccess.vb`
