Notifications (Phase 5 foundation)¶
Phase 5 lands the foundational notification bus that the new WinUI 3 shell will plug into. The goal is to let background components announce state changes (“retention export finished”, “detector trip”, etc.) without depending on UI assemblies.
Responsibilities¶
- Provide a lightweight, in-process publisher that anyone can inject via DI.
- Keep payloads immutable so multiple subscribers (toast host, history pane, logging adapter) can react safely.
- Remain dependency free; callers light up logging/config on their side.
Contracts and service¶
NotificationLevel– severity (Info, Success, Warning, Error) so the UI can map to color/severity treatments.Notification– record withTitle(required), optionalMessage,IconKey, andWhentimestamp (defaults toDateTimeOffset.UtcNow).INotificationService– exposes aNotificationPublishedevent plusPublishto raise a notification.NotificationService– default implementation that stores no state and fires the event synchronously on the calling thread.
Publishing flow¶
- Publisher constructs a
Notification(localize titles before publishing and keep the message concise). - Call
Publish. The default implementation immediately raisesNotificationPublishedso subscribers get the payload while it is still on the stack. - Subscribers (HubWindow view models, tray popups, history backlog, logging bridges) react by enqueueing messages, triggering a toast, or logging the action.
- When running headless (e.g., Supervisor), subscribe with a small adapter that logs or forwards the payload to telemetry so important events do not vanish.
var svc = new NotificationService();
svc.NotificationPublished += (_, n) => Console.WriteLine($"[{n.Level}] {n.Title}");
svc.Publish(new Notification(NotificationLevel.Info, "Saved", IconKey: "Icon.Completed.Glyph"));
Threading and reliability notes¶
- Publishing is synchronous; fire-and-forget expensive UI work on a dispatcher to keep publishers responsive.
- Event handlers must swallow/handle their own exceptions—throwing will bubble back to the publisher.
- For background workflows, wrap
Publishin retry/backoff when the downstream host might be momentarily unavailable.
Testing and DI guidance¶
- Use a simple test double for
INotificationServiceto assert what publishers emit without spinning up the UI shell. - The DI container should expose a single singleton instance so every view model gets the same event stream.
- Unit-test subscribers to ensure severity-specific styling stays in sync with
NotificationLevelvalues.
Future phases will bolt the WinUI 3 toast host, history center, and persistence on top of this contract. Do not introduce UI-specific dependencies here; keep the surface purely domain level so additional hosts (CLI, Supervisor) can continue consuming it.