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

68 lines
3.1 KiB
Markdown

---
status: Accepted
date: 2026-07-03
applyTo:
- "Fuchs/Notifications/**"
- "Fuchs/Services/**"
- "Fuchs/Controllers/**"
supersededBy: ""
---
# 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](0002-gui-notification-delivery-signalr.md) 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.