Skip to content

SAIF.Application architecture

SAIF.Application is the domain-neutral application layer foundation of the LegalMaintenance solution. It sits between the SAIF.BOF.Framework packages below and the domain-specific application layers (SAIF.Application.Claim → SAIF.Application.Claim.Legal → the LegalMaintenance WebForms front end) above, supplying the manager lifecycle contract, access-classification state machine, authorization result caching, data-portal provisioning, message accumulation, and a service-style response envelope, but no domain knowledge, SQL, or UI logic of its own.

Responsibilities

  • Application manager contract (AppManagerBase) — defines the lifecycle template every application process follows: construct-with-identity → Initialize → context validation → security evaluation → Retrieve / Write with transaction hooks. The base class supplies state and helpers; all behavior is deferred to MustOverride members.
  • Access classification state machine (AppSecurityRole + Mark* methods + Is* properties) — a single tri-state value (NoAccess / ViewOnly / Update) from which all coarse-grained UI capability decisions are derived. The state defaults to NoAccess (deny by default).
  • Authorization result caching (UserAccess) — a per-instance memo over clsSecurityPrincipal.IsAuthorized so repeated AD resource checks during a request do not re-query the security service.
  • Data-portal provisioning (ApplicationDataAccess) — builds Oracle or SQL Server data portals from plain connection properties and exposes them as a keyed IDataPortalInfo registry, so a single manager can address multiple databases by name.
  • User-message accumulation and formatting (AppManagerBase.Message / AppendMessageLines, MessageFormatting) — one message buffer per manager, de-duplicated, with a swappable line separator for web vs. console rendering.
  • Service-style response envelope (clsServiceAppManagerBase, ServiceResponse, ServiceMessage, ServiceMessageType) — a second, lighter manager style for service/API callers, where the data portal is injected and results are returned as a categorized message set rather than mutated manager state.

Component boundaries

  • AppManagerBase: stateful, security-aware manager base — owns identity binding, its own data layer (lazily built from SecurityPrincipal.UserName), message buffer, and the AppSecurityRole classification. Only a Protected constructor exists, so no manager instance can be created without an identity.
  • UserAccess: authorization-decision cache layer — binds to a clsSecurityPrincipal via SetPrincipal and memoizes HasAccess results in a case-insensitive SortedList(Of String, Boolean). Contains no knowledge of any specific AD resource name; resource names are supplied entirely by callers.
  • ApplicationDataAccess: data-portal provisioning boundary — builds clsSQLDataPortal / clsMSOracleDataPortal instances from connection properties and registers them in an IDataPortalInfo registry. The DPI setter is Friend, so only AppManagerBase.InjectDPI (same assembly) can register a portal handle from outside the class itself.
  • clsServiceAppManagerBase / ServiceResponse / ServiceMessage / ServiceMessageType: a parallel, stateless manager style for service/API callers — the data portal is injected rather than owned, and it shares no base type with AppManagerBase; it carries none of the access-classification or messaging behavior of the stateful variant.
  • AppConstants / MessageFormatting: shared constants (only SAIFVALUE = "SAIF", no domain literal) and message-line formatting helpers used by both manager styles.

Interaction model

  • Domain application layers (SAIF.Application.Claim, then SAIF.Application.Claim.Legal) inherit AppManagerBase, UserAccess, and AppConstants to add domain-specific managers, resource names, and constants; SAIF.Application itself has no reference to any domain concept.
  • A derived manager is constructed with a clsSecurityPrincipal, then follows the Initialize → context-validation (OnValidContext) → security-evaluation (OnCheckSecurity) → Retrieve / Write lifecycle defined by AppManagerBase; OnCheckSecurity and OnValidContext are declared MustOverride and are never invoked from within this library — invocation and the resulting Mark* classification call are the derived manager's responsibility.
  • Authorization questions are delegated outward: UserAccess.HasAccess / CheckAccess forward caller-supplied resource names verbatim to m_Principal.IsAuthorized (from the SAIF.BOF.Framework.Security package) and cache the Boolean result; the library performs no AD, LDAP, or WCF call itself.
  • Data access is delegated outward as well: ApplicationDataAccess only builds and registers Oracle/SQL Server data portals (clsSQLDataPortal, clsMSOracleDataPortal); it issues no SQL and applies no query, all of which is authored by consumers holding a portal handle.
  • Service-style callers instead construct a clsServiceAppManagerBase-derived type with an injected DPI and receive a ServiceResponse containing categorized ServiceMessage entries, bypassing the AppManagerBase lifecycle entirely.

The following diagram shows high-level component interactions within and around this library.

flowchart TD
    UI[LegalMaintenance WebForms UI] --> Legal[SAIF.Application.Claim.Legal]
    Legal --> Claim[SAIF.Application.Claim]
    Claim --> AMB[AppManagerBase]
    Claim --> UA[UserAccess]
    AMB --> ADA[ApplicationDataAccess]
    AMB --> UA
    UA --> SP[clsSecurityPrincipal<br>SAIF.BOF.Framework.Security]
    ADA --> DP[clsSQLDataPortal / clsMSOracleDataPortal<br>SAIF.BOF.Framework.Connection]
    SVC[clsServiceAppManagerBase] --> ADA

Sequence diagram

The following diagram shows the primary manager construction and authorization-check sequence.

sequenceDiagram
    participant Consumer as Derived manager
    participant AMB as AppManagerBase
    participant UA as UserAccess
    participant SP as clsSecurityPrincipal
    Consumer->>AMB: New(inSecurityPrincipal)
    Consumer->>UA: SetPrincipal(inSecurityPrincipal)
    Consumer->>AMB: Initialize
    Consumer->>UA: HasAccess(resourceName)
    UA->>UA: check cache (m_AccessControl)
    UA->>SP: IsAuthorized(resourceName)
    SP-->>UA: Boolean result
    UA-->>Consumer: cached Boolean
    Consumer->>AMB: MarkUpdate / MarkViewOnly / MarkNoAccess

Design constraints

  • No domain knowledge is embedded in this library; only AppConstants.SAIFVALUE carries an organizational literal, and no claim, legal, policy, or provider concept appears anywhere in the source.
  • No SQL is authored and no direct database call is issued; the library only provisions connections via ApplicationDataAccess.
  • No UI dependency exists at compile time, although System.Web is referenced and <BR> is the default message-line separator, so the message helpers are HTML-aware by convention.*
  • The ApplicationDataAccess.DPI registry has a Friend setter — only AppManagerBase.InjectDPI (same assembly) can register a portal handle from outside the class; external callers must go through CreateOraclePortal / CreateSQLServerPortal or a derived manager.
  • Two parallel manager styles (AppManagerBase and clsServiceAppManagerBase) share no base type and carry different capability sets, so consumers must choose one lineage per manager and cannot mix behavior across them.
  • Authorization is delegated/pass-through: this library caches and exposes decisions produced by clsSecurityPrincipal.IsAuthorized and performs no AD, LDAP, or WCF call itself; whether that call is a WCF SecurityService hop, a direct AD lookup, or a framework-level cache is not determinable from this repository.**
  • Access classification defaults to NoAccess (deny by default) until an explicit Mark* call grants ViewOnly or Update; SecurityRole and its derived Is* properties are read-only, so no external caller can elevate its own rights.
  • UserAccess has no default m_Principal; calling HasAccess before SetPrincipal throws a NullReferenceException rather than failing via an explicit deny.
  • The authorization cache is per-UserAccess-instance and keyed on the lower-cased resource name; a mid-session AD group change is not observed until a new instance is created.

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

  • SAIF.Application/AppManagerBase.vb
  • SAIF.Application/UserAccess.vb
  • SAIF.Application/ApplicationDataAccess.vb
  • SAIF.Application/clsServiceAppManagerBase.vb
  • SAIF.Application/Service Based/*.vb
  • SAIF.Application/AppConstants.vb
  • SAIF.Application/Enums.vb
  • SAIF.Application/MessageFormatting.vb
  • SAIF.Application/SAIF.Application.vbproj
  • SAIF.Application/Service Based/ServiceEnums.vb
  • SAIF.Application/Service Based/ServiceResponse.vb
  • SAIF.Application.Claim/ClaimAppManager.vb