Skip to content

SAIF .NET Core Framework — Architecture

Overview

This repository is a mono-repo containing all of the independently-versioned, independently-built class libraries that together make up the SAIF .NET Core (net6.0/net8.0) application framework. Each folder under src/ is one framework component and is published as its own NuGet package via its own pipeline (see Pipelines.md). There are no project-to-project references between these components — every dependency between the libraries is a NuGet package reference, resolved at build time from the SAIF NuGet feed, exactly as when each component lived in its own repository. Consolidating into one repository changes where the source lives and how it is reviewed; it does not change the packaging or dependency model.

The framework is organized into three conceptual layers:

  1. SAIF.NetCore.ServiceContracts — the foundation layer. Contains shared interfaces and POCO/primitive types used across the framework (e.g. request/response contracts, common abstractions). It has no dependencies on any other SAIF package — it only depends on third-party/framework packages (e.g. Microsoft.IdentityModel.Tokens, Microsoft.AspNetCore.App). Every other library sits on top of it.

SAIF.NetCore.Common plays a similar "no SAIF dependencies" role, providing shared extension methods, enums, and common types that any library (including contracts consumers) can use without pulling in the rest of the framework.

  1. Implementation libraries — the middle layer. Each of these packages implements one focused capability (auth, logging, DB connectivity, HTTP clients, messaging, etc.). They reference ServiceContracts (and sometimes Common) to implement the shared interfaces/POCOs, but they do not reference each other's source — any cross-library reuse is itself just another NuGet package reference. In practice, most implementation libraries only depend on ServiceContracts, but a small number take a NuGet dependency on another implementation package when it makes sense to reuse a capability (see "Notable exceptions" below).

  2. SAIF.NetCore.ServiceExtensions — the parent-most / top layer. Consuming applications only need to add a reference to this single NuGet package to get access to all SAIF services. It contains ASP.NET Core startup base classes and IServiceCollection/IApplicationBuilder extension methods that wire the contracts and implementations together (dependency injection registration, middleware, OpenTelemetry, auth, etc.). It references both ServiceContracts and the implementation packages it composes.

Notable exceptions (implementation → implementation)

A few implementation packages take a direct NuGet dependency on another implementation package rather than only on ServiceContracts, because they build on that package's capability:

  • SAIF.NetCore.NtlmApiAuth → SAIF.NetCore.AuthService
  • SAIF.NetCore.SystemHash → SAIF.NetCore.HashUtil
  • SAIF.NetCore.Services.Messaging → SAIF.NetCore.Logging, SAIF.NetCore.PingableService

These are still package references, not source/project references — each component remains independently buildable and versioned, even though the source now lives side by side in this repository.

Components in this repository

Folder (src/...) NuGet package Role
src/SAIF.NetCore.ServiceContracts SAIF.NetCore.ServiceContracts Foundation: shared interfaces & POCOs
src/SAIF.NetCore.Common SAIF.NetCore.Common Foundation: shared enums/extensions/types
src/SAIF.NetCore.AuthService SAIF.NetCore.AuthService Implementation: authentication
src/SAIF.NetCore.DbConnectionClient SAIF.NetCore.DBConnectionClient Implementation: DB connection client
src/SAIF.NetCore.EnvironmentClient SAIF.NetCore.EnvironmentClient Implementation: environment/config client
src/SAIF.NetCore.ErrorHandler SAIF.NetCore.ErrorHandler Implementation: centralized error handling
src/SAIF.NetCore.HashUtil SAIF.NetCore.HashUtil Implementation: hashing utilities
src/SAIF.NetCore.HttpClients SAIF.NetCore.HttpClients Implementation: HTTP client helpers
src/SAIF.NetCore.LegacyAuth SAIF.NetCore.LegacyAuth Implementation: legacy authentication support
src/Messaging SAIF.NetCore.Services.Messaging Implementation: messaging/queue services
src/SAIF.NetCore.Nhibernate SAIF.NetCore.NHibernate Implementation: NHibernate data access
src/SAIF.NetCore.NtlmApiAuth SAIF.NetCore.NtlmApiAuth Implementation: NTLM API authentication
src/SAIF.NetCore.OIDC SAIF.NetCore.OIDC Implementation: OpenID Connect support
src/SAIF.NetCore.PingableService SAIF.NetCore.PingableService Implementation: health/ping endpoints
src/SAIF.NetCore.Logging SAIF.NetCore.Logging Implementation: logging
src/SAIF.NetCore.SystemHash SAIF.NetCore.SystemHash Implementation: system hashing
src/SAIF.NetCore.Clients.UC4 SAIF.NetCore.Clients.UC4 Implementation: UC4/Automic client
src/SAIF.NetCore.WindowsService SAIF.NetCore.WindowsService Implementation: Windows Service hosting
src/SAIF.NetCore.ServiceExtensions SAIF.NetCore.ServiceExtensions Top layer: app startup/DI wiring, consumed directly by applications

Architecture Diagram

All arrows are dashed to emphasize that every dependency shown is a NuGet package reference, never a project/source reference. Implementation libraries are grouped into themed subgraphs purely for readability — libraries within the same subgraph do not depend on one another (except where an explicit arrow says otherwise).

flowchart TB
    %% Foundation layer
    subgraph Foundation["Foundation layer (no SAIF dependencies)"]
        direction LR
        SC["SAIF.NetCore.ServiceContracts"]
        COMMON["SAIF.NetCore.Common"]
    end

    %% Implementation layer, split into themed groups so no single row gets too wide
    subgraph AuthGroup["Auth & Identity"]
        direction LR
        AUTH["SAIF.NetCore.AuthService"]
        NTLM["SAIF.NetCore.NtlmApiAuth"]
        LEGACY["SAIF.NetCore.LegacyAuth"]
        OIDC["SAIF.NetCore.OIDC"]
    end

    subgraph DataGroup["Data, Messaging & Ops"]
        direction LR
        DB["SAIF.NetCore.DBConnectionClient"]
        NH["SAIF.NetCore.NHibernate"]
        MSG["SAIF.NetCore.Services.Messaging"]
        PING["SAIF.NetCore.PingableService"]
        LOG["SAIF.NetCore.Logging"]
    end

    subgraph UtilGroup["Utilities & Infra"]
        direction LR
        ENV["SAIF.NetCore.EnvironmentClient"]
        ERR["SAIF.NetCore.ErrorHandler"]
        HASH["SAIF.NetCore.HashUtil"]
        SYSHASH["SAIF.NetCore.SystemHash"]
        HTTP["SAIF.NetCore.HttpClients"]
        UC4["SAIF.NetCore.Clients.UC4"]
        WINSVC["SAIF.NetCore.WindowsService"]
    end

    %% Top layer
    SE["SAIF.NetCore.ServiceExtensions (startup base classes & DI extensions)"]
    APP["Consuming Application"]

    %% Each themed group depends on the foundation (NuGet package references)
    AuthGroup -.-> Foundation
    DataGroup -.-> Foundation
    UtilGroup -.-> Foundation

    %% Notable implementation-to-implementation NuGet references
    NTLM -.->|NuGet ref| AUTH
    SYSHASH -.->|NuGet ref| HASH
    MSG -.->|NuGet ref| LOG
    MSG -.->|NuGet ref| PING

    %% ServiceExtensions composes contracts + implementations
    SE -.-> Foundation
    SE -.-> AuthGroup
    SE -.-> DataGroup
    SE -.-> UtilGroup

    %% Applications consume ServiceExtensions
    APP -.->|NuGet ref| SE

Key Takeaways

  • Direction of dependency is strictly bottom-up: ServiceContracts/Common → implementation libraries → ServiceExtensions → applications. Nothing in a lower layer ever references a higher layer.
  • All cross-component dependencies are NuGet package references, not project references — each component still builds, versions, and ships independently even though its source lives in this shared repository.
  • Implementation libraries are siblings: by convention they depend only on the foundation layer, not each other, keeping them independently swappable. The small set of exceptions listed above are intentional NuGet dependencies where one capability is built directly on another.
  • Applications only need to reference SAIF.NetCore.ServiceExtensions to pull in the full set of startup wiring for the SAIF framework; the transitive NuGet graph brings in the implementation packages and ServiceContracts automatically.
  • This repository replaced 19 standalone repositories (each named Framework.NCore.<Component>) as part of a mono-repo migration; see Pipelines.md for the current per-component pipeline setup.