- 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.
68 lines
3.1 KiB
Markdown
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.
|