Define per-component configuration binding convention in Host

Expand REQ-HOST-3 with per-component appsettings.json sections, each mapped
to a dedicated options class owned by the component. Convention: components
define their own options class, Host binds via Options pattern, components
read via IOptions<T>. Options classes live in component projects, not Commons.
This commit is contained in:
Joseph Doherty
2026-03-16 09:33:05 -04:00
parent 4ec5d50425
commit f5b3b2b59e

View File

@@ -39,11 +39,37 @@ Components not applicable to the current role must not be registered in the DI c
### REQ-HOST-3: Configuration Binding
The Host must bind configuration sections from `appsettings.json` to strongly-typed options classes using the .NET Options pattern:
The Host must bind configuration sections from `appsettings.json` to strongly-typed options classes using the .NET **Options pattern** (`IOptions<T>` / `IOptionsSnapshot<T>`). Each component has its own configuration section under `ScadaLink`, mapped to a dedicated configuration class owned by that component.
- `ScadaLink:Node` section bound to `NodeConfiguration` (Role, NodeHostname, SiteId, RemotingPort).
- `ScadaLink:Cluster` section bound to `ClusterConfiguration` (SeedNodes, SplitBrainResolverStrategy, StableAfter).
- `ScadaLink:Database` section bound to `DatabaseConfiguration` (Central: ConfigurationDb, MachineDataDb connection strings; Site: SQLite paths).
#### Infrastructure Sections
| Section | Options Class | Owner | Contents |
|---------|--------------|-------|----------|
| `ScadaLink:Node` | `NodeOptions` | Host | Role, NodeHostname, SiteId, RemotingPort |
| `ScadaLink:Cluster` | `ClusterOptions` | ClusterInfrastructure | SeedNodes, SplitBrainResolverStrategy, StableAfter, HeartbeatInterval, FailureDetectionThreshold, MinNrOfMembers |
| `ScadaLink:Database` | `DatabaseOptions` | Host | Central: ConfigurationDb, MachineDataDb connection strings; Site: SQLite paths |
#### Per-Component Sections
| Section | Options Class | Owner | Contents |
|---------|--------------|-------|----------|
| `ScadaLink:DataConnection` | `DataConnectionOptions` | Data Connection Layer | ReconnectInterval, TagResolutionRetryInterval, WriteTimeout |
| `ScadaLink:StoreAndForward` | `StoreAndForwardOptions` | Store-and-Forward | SqliteDbPath, ReplicationEnabled |
| `ScadaLink:HealthMonitoring` | `HealthMonitoringOptions` | Health Monitoring | ReportInterval, OfflineTimeout |
| `ScadaLink:SiteEventLog` | `SiteEventLogOptions` | Site Event Logging | RetentionDays, MaxStorageMb, PurgeScheduleCron |
| `ScadaLink:Communication` | `CommunicationOptions` | Communication | DeploymentTimeout, LifecycleTimeout, QueryTimeout, TransportHeartbeatInterval, TransportFailureThreshold |
| `ScadaLink:Security` | `SecurityOptions` | Security & Auth | LdapServer, LdapPort, LdapUseTls, JwtSigningKey, JwtExpiryMinutes, IdleTimeoutMinutes |
| `ScadaLink:InboundApi` | `InboundApiOptions` | Inbound API | DefaultMethodTimeout |
| `ScadaLink:Notification` | `NotificationOptions` | Notification Service | (SMTP config is stored in config DB and deployed to sites, not in appsettings) |
| `ScadaLink:Logging` | `LoggingOptions` | Host | Serilog sink configuration, log level overrides |
#### Convention
- Each component defines its own options class (e.g., `DataConnectionOptions`) in its own project. The class is a plain POCO with properties matching the JSON section keys.
- The Host binds each section during startup via `services.Configure<T>(configuration.GetSection("ScadaLink:<ComponentName>"))`.
- Each component's `AddXxx()` extension method accepts `IServiceCollection` and reads its options via `IOptions<T>` — the component never reads `IConfiguration` directly.
- Options classes live in the component project, not in Commons, because they are component-specific configuration — not shared contracts.
- Startup validation (REQ-HOST-4) validates all required options before the actor system starts.
### REQ-HOST-4: Startup Validation