Files
Fuchs_Intranet/Fuchs/Docs/Decisions/0001-domain-events-and-notification-triggers.md
Stefan 882e97509a Enhance logging in FdsSqlOptions and related classes
- Updated FdsSqlOptions to accept an optional ILogger parameter for improved error logging.
- Modified FdsMfr and FdsMfrClient classes to pass the logger instance to FdsSqlOptions.
- Added detailed error logging in various methods to capture SQL execution issues and file handling errors.
- Improved documentation for FdsSqlOptions to clarify logging behavior.
- Updated Archive class to log compression errors, enhancing traceability of failures.
- Adjusted project configuration to suppress specific warnings related to transitive dependencies.
- Added NuGet.config to define package sources for dependency management.
- Updated submodule references for OCORE and related projects.
2026-07-03 20:22:05 +02:00

3.1 KiB

status, date, applyTo, supersededBy
status date applyTo supersededBy
Accepted 2026-07-03
Fuchs/Notifications/**
Fuchs/Services/**
Fuchs/Controllers/**

0001 — Domain events (success and failure) trigger user-understandable notifications

Context

Business operations (invoice creation, sending, marking sent, reminders, banking import) happen server-side, often outside a synchronous request the user is watching (background jobs, long-running sends). Users had no reliable way to learn that an operation they cared about — or one that failed — actually happened, short of refreshing lists or checking logs.

Decision

Every meaningful business outcome, success and failure, is modeled as a DomainEvent (Fuchs/Notifications/DomainEvent.cs) with:

  • a DomainEventType enum value identifying what happened,
  • the acting UserAccountId,
  • a Title, and
  • a Context dictionary of the data needed to render a human-readable message (invoice number, email address, file name, row counts, etc.).

Services call the corresponding method on IEventService (Fuchs/Notifications/IEventService.cs, implemented by EventService) at the point the outcome is known — e.g. InvoiceSentToCustomerAsync(invoice, email, userAccountId) or InvoiceIssueAsync(message, userAccountId, invoiceId) on failure. EventService.PublishAsync renders the event into a GuiNotification with a German, end-user-readable Message (e.g. "Rechnung R2026-0001 wurde an den Kunden mit der E-Mail test@test.de versandt.") and pushes it — see 0002 for delivery.

Every new business operation with a user-visible outcome (created, sent, failed, imported, etc.) must add a DomainEventType value and a matching IEventService method, and call it from the service at the point of success and the point of failure.

Consequences

  • IEventService is injected into services that perform user-facing operations (InvoiceService, ReminderService, BankingService callers) — never bypass it by writing directly to NotificationHub.
  • Failure paths must call the *IssueAsync/*Failed event too, not just succeed-path events — silent failures are the problem this exists to prevent.
  • Messages are built server-side in EventService.BuildNotification, in German, using only Context values — keep Context populated with everything the message needs (don't rely on the client to look anything up).
  • Adding a new event type means updating the enum, the IEventService interface + EventService implementation (trigger method + message branch + IsFailure if it's a failure type), and the calling service — in the same change.

Alternatives considered

  • Polling a status endpoint from the client: rejected — adds latency, extra load, and doesn't generalize to background/multi-tab flows as cleanly as a push model.
  • Raw exception messages surfaced to the GUI: rejected — not user-understandable and leaks internal details; Context + a rendered German message keeps the boundary between internal errors and user-facing text explicit.