Skip to content

SAIF.Framework.Windows architecture

SAIF.Framework.Windows (packaged as SAIF.Framework.Windows, assembly SAIF.Framework.Console, root namespace SAIF.Console) is a Framework4-classified, generic console/batch-application composition framework: it supplies the entry-point orchestration, command-line argument parsing/validation, console logging, and Autofac-based NHibernate unit-of-work DI wiring that a consuming console application builds on top of, but it defines no concrete application, entity mappings, or authorization logic of its own.

Responsibilities

  • Provide an entry-point orchestrator (ProgramRunnerBase<TApplication, TArgumentContainer> and its standardized subclass SaifStandardProgramBase<TApp, TArgContainer>) that resolves command-line arguments, builds the Autofac composition root, resolves the concrete application, and translates exceptions to process exit codes.
  • Provide an application lifecycle base (ConsoleBase<TArgContainer>) that owns the Initialize()/Execute()/Finish() lifecycle, run timing (StartTime/EndTime/RunningTime), and console header/footer output.
  • Provide a reflection-driven command-line argument parsing and validation subsystem (ICommandLineParser → PatternCommandLineParser → SaifStandardCommandLineParser, paired with ArgumentContainerBase<T> → StandardArgumentContainerBase<T>, ArgumentResolverBuilder<T>, and ArgumentResolver<T, TArgument>).
  • Provide a console/file logging subsystem (IConsoleLogService/ConsoleLogService, ILogWriter/LogWriter) that discovers machine-scoped file0–file4 environment variables and registers one log writer per match.
  • Provide a data-access composition subsystem (DatabaseConfig/OracleConfig/SqlServerConfig and AutofacExtensions.RegisterCommonDependencies) that registers named NHibernate unit-of-work factories (Oracle stateful, Oracle stateless, SQL Server) and per-instance unit-of-work registrations into the consuming application's Autofac container.
  • Provide a typed exception/exit-code hierarchy (SaifConsoleException and its subclasses BadParameterException, CustomErrorException, DatabaseErrorException, DataErrorException, FileInputOutputException, NoDataFoundException) consumed by ProgramRunnerBase.Run()'s catch block to set Environment.ExitCode.

Component boundaries

  • Entry-point/composition layer (ProgramRunnerBase<TApplication, TArgumentContainer>, SaifStandardProgramBase<TApp, TArgContainer>): owns argument-container construction, argument validation gating, the Autofac ContainerBuilder composition root, application resolution, and top-level exception-to-exit-code translation. Never touches application-specific logic — that is delegated to abstract members (CreateApplication, CreateArgumentContainer, RegisterAppDependencies, RawArguments).
  • Application layer (ConsoleBase<TArgContainer>): owns the Initialize()/Execute()/Finish() lifecycle and console header/footer logging via an injected IConsoleLogService; all concrete behavior is supplied by a consuming subclass.
  • Argument-parsing subsystem (ICommandLineParser/PatternCommandLineParser/SaifStandardCommandLineParser, ArgumentContainerBase<T>/StandardArgumentContainerBase<T>, ArgumentResolverBuilder<T>/ArgumentResolver<T, TArgument>, internal IArgumentResolver<T>): parses raw string[] arguments into a dictionary, then binds/validates them onto a consuming application's argument container via explicit or reflection-driven resolvers.
  • Logging subsystem (IConsoleLogService/ConsoleLogService, ILogWriter/LogWriter): discovers machine-level environment variables matching ^file[0-4] and wraps each in a LogWriter keyed by a fixed LogType lookup, in addition to console output.
  • Data-access composition subsystem (DatabaseConfig/OracleConfig/SqlServerConfig, AutofacExtensions.RegisterCommonDependencies): registers three named IUnitOfWorkFactory instances ("oracle", "oracleStateless", "sqlSever") and, per supplied DatabaseConfig, a named IUnitOfWork/IRepositoryFactory registration into the same Autofac container used by the entry-point layer. This is the single integration point where the DI/composition layer, the data-access subsystem, and core framework services (IEnvironmentService, IConsoleLogService) are wired together.
  • Exception/exit-code subsystem (SaifConsoleException and its typed subclasses): each carries a fixed or caller-supplied ExitCode consumed by ProgramRunnerBase.Run()'s catch block.
  • No entity/domain layer: this library defines no NHibernate entity mappings (ClassMap<T>) itself — dataClassMapAssemblies is a pass-through parameter forwarded to OracleConfigurationFactory/SqlServerConfigurationFactory (both external, from SAIF.Framework.Data.NHibernate); the consuming application supplies its own mapping assemblies.

Interaction model

  • A consuming console application's Main() constructs a ProgramRunnerBase/SaifStandardProgramBase subclass and calls Run().
  • Run() first builds Arguments = CreateArgumentContainer(RawArguments) (SaifStandardProgramBase supplies Environment.GetCommandLineArgs()), then calls Arguments.ResolveArguments(), which parses raw arguments via the container's ICommandLineParser and binds/validates them through the ArgumentResolverBuilder/ArgumentResolver pipeline.
  • If argument resolution is invalid, Run() writes errors to Console.Error, sets Environment.ExitCode = 1, blocks on Console.ReadLine(), and returns without registering dependencies, building the container, or running the application.
  • If valid, Run() calls the abstract RegisterAppDependencies(builder) (the consuming application's own Autofac composition-root override, which is expected to call AutofacExtensions.RegisterCommonDependencies), registers CreateApplication as a component factory, builds the Autofac container, and resolves the concrete TApplication (a ConsoleBase<TArgContainer> subclass).
  • Run() resolves IUserProfileService and IEnvironmentService from the container to print the current user and application track, then calls app.Run(), which executes the ConsoleBase Initialize()/Execute()/Finish() lifecycle (resetting Environment.ExitCode = 0 after a successful Initialize()).
  • Exceptions escaping resolution/app.Run() are caught: a SaifConsoleException sets Environment.ExitCode from its own ExitCode property; any other exception sets a fixed exit code of 5. SaifStandardProgramBase.Run() additionally always prints the final exit code.
  • AutofacExtensions.RegisterCommonDependencies(builder, currentUserId, dataClassMapAssemblies, params DatabaseConfig[] configs) is the composition entry point a consuming application's RegisterAppDependencies override calls to register: the three named NHibernate unit-of-work factories, one named IUnitOfWork/IRepositoryFactory registration per supplied DatabaseConfig, the external WindowsEnvironmentService as IEnvironmentService, ServiceRegistrations.RegisterCoreServicesForWindows, and ConsoleLogService as IConsoleLogService.
  • Each DatabaseConfig.RegisterUnitOfWork resolves IEnvironmentService at container-build time to compute a connection string (GetConnectionString(env, env.GetConfigSetting(EnvironmentConfigKey))), delegating to the external DbConnectionManager.GetConnectionStringForInstance (Oracle) or GetConnectionStringForSqlServer (SQL Server).

Architecture diagram

The following diagram shows how a consuming console application composes with this framework's entry-point, argument, logging, and data-access-composition subsystems.

flowchart TD
    App[Consuming console application<br>Main] --> SPB[SaifStandardProgramBase /<br>ProgramRunnerBase]
    SPB --> ACB[ArgumentContainerBase /<br>StandardArgumentContainerBase]
    ACB --> CLP[ICommandLineParser<br>PatternCommandLineParser /<br>SaifStandardCommandLineParser]
    ACB --> ARB[ArgumentResolverBuilder /<br>ArgumentResolver]
    SPB --> AFX[AutofacExtensions.<br>RegisterCommonDependencies]
    AFX --> DBC[DatabaseConfig<br>OracleConfig / SqlServerConfig]
    DBC --> UOW[NHibernate unit-of-work factories<br>oracle / oracleStateless / sqlSever]
    AFX --> ENV[WindowsEnvironmentService<br>As IEnvironmentService]
    AFX --> CLS[ConsoleLogService<br>As IConsoleLogService]
    SPB --> CB[ConsoleBase<br>Initialize / Execute / Finish]
    CB --> CLS
    CLS --> LW[LogWriter instances<br>file0-file4 / Custom]
    SPB --> EXC[SaifConsoleException hierarchy<br>maps to Environment.ExitCode]

Sequence diagram

The following diagram shows the primary Run() request/response sequence from process start to exit-code assignment.

sequenceDiagram
    participant Main as Consuming app Main
    participant PRB as ProgramRunnerBase.Run
    participant ACB as ArgumentContainerBase
    participant Container as Autofac container
    participant App as ConsoleBase.Run
    Main->>PRB: Run()
    PRB->>ACB: CreateArgumentContainer + ResolveArguments()
    ACB-->>PRB: ValidationResult
    alt invalid arguments
        PRB-->>Main: ExitCode=1, Console.Error, ReadLine block
    else valid arguments
        PRB->>Container: RegisterAppDependencies + Build()
        Container-->>PRB: TApplication instance
        PRB->>App: app.Run()
        App-->>PRB: Initialize/Execute/Finish complete
        PRB-->>Main: ExitCode from SaifConsoleException or 0/5
    end

Design constraints

  • ProgramRunnerBase never contains application-specific logic; all application behavior is delegated to the abstract Initialize/Execute/Finish triplet on ConsoleBase and the abstract CreateApplication/CreateArgumentContainer/RegisterAppDependencies/RawArguments members on itself — this library defines only generic scaffolding.
  • Argument validation always runs before any application dependency registration, container build, or application resolution (BR-WIN-001); on invalid arguments the process halts and requires a console keypress before exiting (BR-WIN-003).
  • Exit-code assignment is exception-type-driven: a SaifConsoleException (or subclass) supplies its own ExitCode; any other unhandled exception is a fixed exit code of 5 (BR-WIN-004/BR-WIN-005); a successful Initialize() always resets Environment.ExitCode to 0 before Execute() runs (BR-WIN-006).
  • Argument-container properties are either explicitly configured via BuildArgumentResolvers, auto-resolved via reflection when IConvertible (BR-WIN-007), or flagged as a validation failure when not convertible and unconfigured (BR-WIN-008) — there is no silent-skip path.
  • AutofacExtensions.RegisterCommonDependencies is the single integration point wiring the DI/composition layer, the data-access subsystem, and core framework services together; a consuming application must call it from its own RegisterAppDependencies override.
  • AutofacExtensions.RegisterCommonDependencies unconditionally registers WindowsEnvironmentService As<IEnvironmentService>() — the concrete environment-service implementation is fixed by this framework, not independently configurable per consuming application short of re-registration.
  • SQL Server unit-of-work registration has no stateless-session variant: SqlServerConfig.FactoryName is always the single "sqlSever" factory (name typo preserved verbatim from source), regardless of the inherited Stateless flag (BR-WIN-015); Oracle's Stateless flag does select between "oracle" and "oracleStateless" (BR-WIN-016).
  • This library defines no NHibernate entity mappings and performs no credential retrieval of its own; mapping assemblies, connection instance/user/server names, and the meaning of EnvironmentConfigKey are all supplied by the consuming application.
  • No authentication/authorization patterns exist in this library; any such concerns are the responsibility of a consuming application's own composition root and business logic.

Sources

  • Console\SAIF.Console\SAIF.Framework.Windows.csproj
  • Console\SAIF.Console\Console\ProgramRunnerBase.cs
  • Console\SAIF.Console\Console\SaifStandardProgramBase.cs
  • Console\SAIF.Console\Console\ConsoleBase.cs
  • Console\SAIF.Console\Console\ICommandLineParser.cs
  • Console\SAIF.Console\Console\PatternCommandLineParser.cs
  • Console\SAIF.Console\Console\SaifStandardCommandLineParser.cs
  • Console\SAIF.Console\Console\Arguments\ArgumentContainerBase.cs
  • Console\SAIF.Console\Console\Arguments\ArgumentResolver.cs
  • Console\SAIF.Console\Console\Arguments\ArgumentResolverBuilder.cs
  • Console\SAIF.Console\Console\Arguments\IArgumentResolver.cs
  • Console\SAIF.Console\Console\Arguments\StandardArgumentContainerBase.cs
  • Console\SAIF.Console\Console\ConsoleLogService.cs
  • Console\SAIF.Console\Console\LogWriter.cs
  • Console\SAIF.Console\Console\AutofacExtensions.cs
  • Console\SAIF.Console\Console\DatabaseConfigs\DatabaseConfig.cs
  • Console\SAIF.Console\Console\DatabaseConfigs\OracleConfig.cs
  • Console\SAIF.Console\Console\DatabaseConfigs\SqlServerConfig.cs
  • Console\SAIF.Console\Console\Exceptions\SAIFConsoleException.cs
  • Console\SAIF.Console\Console\Exceptions\BadParameterException.cs
  • Console\SAIF.Console\Console\Exceptions\CustomErrorException.cs
  • Console\SAIF.Console\Console\Exceptions\DatabaseErrorException.cs
  • Console\SAIF.Console\Console\Exceptions\DataErrorException.cs
  • Console\SAIF.Console\Console\Exceptions\FileInputOutputException.cs
  • Console\SAIF.Console\Console\Exceptions\NoDataFoundException.cs