--- status: Accepted date: 2026-07-15 applyTo: - "Fuchs_DataService/**" - "Fuchs/Services/PeriodicHostedService.cs" - "Fuchs/Program.cs" supersededBy: "" --- # 0010 — MFR ERP sync runs in-process in the web app; Fuchs_DataService is a library ## Context `Fuchs_DataService` was a standalone console/Windows Service hosted by **Topshelf**. It carried its own `appsettings.json`, its own file-based configuration bootstrap (`FdsConfig.Initialize()` reading the file), its own logging provider (`FdsLoggerProvider`/`AddFdsLogging`), and a machine-name guard in `Main()` that disabled the service on developer PCs. In practice the web app (`Fuchs`) already referenced the project, already called `fds.FdsConfig.Initialize(builder.Configuration)`, already registered `IFdsMfr`, and already created `FdsMfrClient` via `IMfrClientFactory` — so the sync logic and the web app were sharing the same code and the same connection strings while the service kept a second, parallel copy of configuration/logging/hosting. Maintaining a separate process, a second `appsettings.json` (duplicating connection strings + MFR credentials), Topshelf, and a machine-name guard added drift risk and operational overhead for no benefit the web host couldn't provide. ## Decision - **`Fuchs_DataService` is now a class library** (no `OutputType Exe`, no Topshelf, no own `appsettings.json`, no `install.bat`/`un-install.bat`, no `System.Configuration.ConfigurationManager`). It contains only the MFR sync logic (`FdsMfr`/`IFdsMfr`, `FdsMfrClient`), the DATEV/zip helpers, `FdsShared`, `FdsDebug`, and `FdsConfig`. - **The host owns configuration.** `FdsConfig` keeps only `Initialize(IConfiguration)` (the file-based overload is gone). The Fuchs web app injects its `IConfiguration`; connection strings (`fuchs_fds_ConnectionString`) and MFR credentials (`Fds:MFR_*`, Key Vault-managed) come from Fuchs. - **The host owns logging.** `FdsLoggerProvider`/`AddFdsLogging` were removed; the library uses only `ILogger`/`ILoggerFactory` injected from Fuchs's logging (`AddFuchsLogging`). The library depends only on `Microsoft.Extensions.Logging.Abstractions` + `Microsoft.Extensions.Configuration.Binder`. - **The sync runs in-process.** `PeriodicHostedService` (the generic multi-job `BackgroundService`) moved to `Fuchs/Services/` and is registered in `Program.cs` as a hosted service. The single `MfrSync` job calls `UpdateIfNecessary_async` → `UpdateRequested_async` → `GetInvoiceFiles_async`. - **A config flag replaces the machine-name guard.** Registration is gated by `Fds:SyncEnabled` (default `false` when unset): `true` in production `appsettings.json`, `false` in `appsettings.Development.json`, so developer machines never poll the ERP. Interval (`Fds:ExecutionFrequency_Minutes`, default 15) and debug verbosity (`Fds:DebugDetails`) also come from the `Fds` section. ## Consequences - One process, one configuration surface, one logging pipeline. The sync inherits the web app's OpenTelemetry, DI, and lifetime automatically. - **Instance fan-out is a consideration:** the sync now runs in *every* web instance where `Fds:SyncEnabled` is true. The intranet is deployed single-instance, so this is acceptable; if Fuchs is ever scaled out, gate the sync to a single instance (leader election / dedicated instance flag) to avoid concurrent MFR polling. - Enabling/disabling the sync per environment is now a config change, not a redeploy of a separate service. - `Fuchs_DataService` is intentionally kept as a separate project (not folded into `Fuchs`) so the sync logic stays isolated and unit-testable; `Fuchs.Tests` covers it via `InternalsVisibleTo`. - **The `Squid-Box.SevenZipSharp` native dependency (and the bundled `7z.dll`) was removed** from both `Fuchs_DataService` and `Fuchs`. The only live archive use — the DATEV export — is a plain, unencrypted zip, now produced via the native `OCORE.zip.filesToZipArchive` (`System.IO.Compression`). The 7-Zip-only paths (`.7z`/LZMA2, AES-encrypted archives, extraction, `FastAppend`) had no callers. Trade-off accepted: `System.IO.Compression` cannot produce `.7z` or password/AES archives; if that is ever required, a compression library must be reintroduced. ## Alternatives considered - **Native `dotnet` Worker Service (separate process).** Would modernize off Topshelf but keep the duplicate-config/duplicate-logging/second-process problem. Rejected because the web app already hosts everything the sync needs. - **Fold the code directly into `Fuchs`.** Rejected to preserve a clean, separately testable sync library and avoid enlarging the web project.