Files
breadcrumb-the-shire/docs/reference-notifications-module.md

116 lines
3.6 KiB
Markdown
Raw Normal View History

# Notifications Modul (V1)
Letzte Aktualisierung: 2026-03-19
## Ziel
Referenz fuer das Modul `notifications`: Event-Registrierung, Erzeugungsregeln, Guardrails, Frontend-Vertrag und Testbarkeit.
## Scope v1
- In-App Notifications (Bell + Dropdown), kein E-Mail-Channel.
- Core-5 Eventtypen:
- `user.created`
- `user.deleted`
- `user.activated`
- `user.deactivated`
- `user.assignment_changed`
- Dedupe fuer diese Typen mit 30 Minuten Fenster.
## Modul-Verkabelung
Kanonische Quelle: `modules/notifications/module.php`
- `event_listeners`
- mappt Eventtyp -> Listenerklasse + Methode `handle`.
- `ui_slots`
- `topbar.right_item`: Bell-Template
- `layout.head_style`: Modul-CSS fuer Topbar + Dropdown
- `runtime.component`: Bell-JS (`initNotificationBell`)
- `permissions`
- `notifications.view`
- `authorization_policies`
- `NotificationsAuthorizationPolicy`
- `layout_context_providers`
- liefert `notifications.nav.unread_count` in den Layout-Context
- `session_providers`
- synchronisiert `module.notifications.unread_count` aus DB
## Event -> Notification Fluss
1. Ein User-Lifecycle-Workflow dispatcht ein Domain-Event (z. B. `user.activated`).
2. Der in `module.php` registrierte Listener verarbeitet das Payload.
3. Listener ruft `NotificationService` auf.
4. `NotificationService` orchestriert Empfaenger/Fan-out und Dedupe-Metadaten.
5. `NotificationRepository` persistiert in `user_notifications`.
6. Bell-Endpunkte lesen tenant-scoped Daten und aktualisieren den Session-`unread_count`.
## Datenvertrag Notification
Persistierter Kernvertrag:
- `type` (string)
- `title` (string)
- `body` (nullable string)
- `link` (nullable string)
- `data` (nullable JSON)
Dedupe-Metadaten (intern):
- `dedupe_fingerprint`
- `dedupe_bucket`
- `dedupe_until`
## Guardrails (Server)
- Alle Bell-Endpunkte erzwingen:
- Login
- Ability `notifications.view`
- POST-Endpunkte (`mark-read`, `delete`) pruefen CSRF.
- Tenant-Scope wird serverseitig aus `current_tenant` erzwungen.
- SQL ausschliesslich im Repository (`NotificationRepository`), Service nur Orchestrierung.
## Guardrails (Frontend)
- Endpunkte werden mit App-Base-URL gebaut (kein harter Root-Pfad).
- Fehlerpfade laufen ueber zentrale Telemetrie (`warn_once`).
- A11y-Klickzeilen im Dropdown sind lokal gestylt, damit globale `[role="button"]` Regeln nicht durchschlagen.
- Default-`details/summary`-Chevron wird pro Komponente ueber `data-summary-chevron="none"` deaktiviert.
## Template/Partial Best Practice
- Core-Partials immer ueber `templatePath('partials/...')` einbinden.
- Keine fragilen relativen Tiefenpfade auf Core-Templates verwenden.
- Modulinterne Templates bleiben lokal relativ zum Modul.
## CSS Token Best Practice (Module)
- Modul-CSS soll auf Core-Variablen aufsetzen (z. B. `--app-color`, `--app-muted-color`, `--app-dropdown-*`, `--app-action-danger-*`).
- Modul-spezifische Aliase sind ok (`--app-notification-*`), solange sie auf Core-Tokens mappen.
- Harte Hex-Fallbacks nur als Ausnahme; Standard ist tokenbasiert.
## Tests
### Unit
- Listener-Tests fuer alle Core-5 Typen:
- Empfaengerregeln
- Actor-Exclusion
- ungultige/leere Payloads
- Service-Tests fuer Dedupe (created vs suppressed)
- Policy-Tests fuer `notifications.view`
### Integration/Flow
- End-to-End fuer create/delete/activate/deactivate/assignment-change.
- Bell-Endpunkte mit Tenant-Scope und Permission-Pruefung.
- Bulk-Flows dispatchen konsistent.
### Manueller Smoke
1. Zwei Admins im selben Tenant, einer als Actor.
2. Actor fuehrt User-Aktion aus.
3. Zweiter Admin sieht Bell-Badge + Listeneintrag.
4. Wiederholung innerhalb 30 Minuten erzeugt kein Duplikat.