- Introduced comprehensive unit tests for the Fuchs_DataService library, covering DATEV header formatting, CSV/XML generation, and FdsMfrClient construction. - Implemented tests for FdsMfr.UpdateNeed parsing and FdsShared utility helpers, ensuring correct functionality and stability. - Added tests for FdsConfig and FdsMfrClient to validate configuration resolution and client construction. Document decisions on backend-authoritative invoice and reminder handling - Created ADR 0008 to clarify that all invoice types and reminder stages are backend-authoritative during drafting and previewing. - Established that all calculations and settings must be processed server-side, ensuring consistency between online editor and PDF outputs. Define irreversible mutations for set-price modes in invoices - Documented ADR 0009 to specify that the "Set mit Preis" and "Nur Set mit Preis" operations are irreversible mutations affecting service request blocks. - Clarified that these operations are not display toggles but actual data changes, ensuring clear expectations for invoice handling. Transition MFR ERP sync to in-process execution within the web app - Created ADR 0010 to outline the migration of Fuchs_DataService from a standalone service to an in-process library within the Fuchs web application. - Updated configuration and logging management to be handled by the host application, streamlining the sync process. Add publish profile and periodic hosted service for job scheduling - Introduced a publish profile for deployment to a specified folder. - Implemented PeriodicHostedService to manage multiple independent jobs, including the MFR ERP sync, with configurable execution intervals. Add dotnet-tools.json for EF Core CLI tools - Included dotnet-tools.json to manage the version of dotnet-ef for Entity Framework Core migrations and commands.
4.6 KiB
status, date, applyTo, supersededBy
| status | date | applyTo | supersededBy | |||
|---|---|---|---|---|---|---|
| Accepted | 2026-07-15 |
|
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_DataServiceis now a class library (noOutputType Exe, no Topshelf, no ownappsettings.json, noinstall.bat/un-install.bat, noSystem.Configuration.ConfigurationManager). It contains only the MFR sync logic (FdsMfr/IFdsMfr,FdsMfrClient), the DATEV/zip helpers,FdsShared,FdsDebug, andFdsConfig.- The host owns configuration.
FdsConfigkeeps onlyInitialize(IConfiguration)(the file-based overload is gone). The Fuchs web app injects itsIConfiguration; connection strings (fuchs_fds_ConnectionString) and MFR credentials (Fds:MFR_*, Key Vault-managed) come from Fuchs. - The host owns logging.
FdsLoggerProvider/AddFdsLoggingwere removed; the library uses onlyILogger/ILoggerFactoryinjected from Fuchs's logging (AddFuchsLogging). The library depends only onMicrosoft.Extensions.Logging.Abstractions+Microsoft.Extensions.Configuration.Binder. - The sync runs in-process.
PeriodicHostedService(the generic multi-jobBackgroundService) moved toFuchs/Services/and is registered inProgram.csas a hosted service. The singleMfrSyncjob callsUpdateIfNecessary_async→UpdateRequested_async→GetInvoiceFiles_async. - A config flag replaces the machine-name guard. Registration is gated by
Fds:SyncEnabled(defaultfalsewhen unset):truein productionappsettings.json,falseinappsettings.Development.json, so developer machines never poll the ERP. Interval (Fds:ExecutionFrequency_Minutes, default 15) and debug verbosity (Fds:DebugDetails) also come from theFdssection.
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:SyncEnabledis 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_DataServiceis intentionally kept as a separate project (not folded intoFuchs) so the sync logic stays isolated and unit-testable;Fuchs.Testscovers it viaInternalsVisibleTo.- The
Squid-Box.SevenZipSharpnative dependency (and the bundled7z.dll) was removed from bothFuchs_DataServiceandFuchs. The only live archive use — the DATEV export — is a plain, unencrypted zip, now produced via the nativeOCORE.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.Compressioncannot produce.7zor password/AES archives; if that is ever required, a compression library must be reintroduced.
Alternatives considered
- Native
dotnetWorker 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.