Files
boilerplate/docs/notifications.md
Bastian Wagner c03b2e17f5 bump
2026-07-16 15:23:53 +02:00

147 lines
4.8 KiB
Markdown

# Benachrichtigungen
Das Modul `apps/backend/src/notifications` stellt persoenliche In-App-
Benachrichtigungen bereit. Es gibt bewusst keinen E-Mail-Versand, keine Push
Notifications, keine WebSockets, keine Server-Sent Events, keine Queue, kein
Redis und keine Hintergrundjobs. Neue Benachrichtigungen werden beim Laden der
Anwendung und per Frontend-Polling abgerufen.
## Datenmodell
Die Entity `NotificationEntity` wird in der Tabelle `notifications` gespeichert.
Jede Benachrichtigung gehoert genau einem Benutzer.
Felder:
- `id`: UUID
- `userId`: Foreign Key auf `users.id`
- `type`: technischer Typ aus `NotificationType`
- `title`: kurzer Plain-Text-Titel, maximal 150 Zeichen
- `message`: Plain-Text-Nachricht, maximal 1000 Zeichen
- `link`: optionale interne Angular-Route, maximal 500 Zeichen
- `metadata`: optionale JSON-Metadaten, maximal 4096 Bytes serialisiert
- `readAt`: `null`, solange ungelesen
- `createdAt`: UTC-Erstellungszeit
- `deletedAt`: Soft Delete
Indizes:
- `user_id, created_at`
- `user_id, read_at`
- `user_id, deleted_at`
Die Migration liegt unter
`apps/backend/src/database/migrations/1720000001000-AddNotifications.ts` und wird
nicht automatisch beim App-Start ausgefuehrt.
## Typen
Benachrichtigungstypen sind Code, keine Datenbankdaten:
- `system`
- `item.created`
- `item.updated`
- `user.role-changed`
Neue Module erweitern `apps/backend/src/notifications/notification-types.ts`
und nutzen anschliessend `NotificationsService`.
## Permissions
- `notifications.readOwn`: eigene Benachrichtigungen lesen
- `notifications.updateOwn`: eigene Benachrichtigungen markieren oder loeschen
- `notifications.manage`: administrativ Benachrichtigungen erzeugen
Die Systemrolle `user` erhaelt `notifications.readOwn` und
`notifications.updateOwn`. Die Rolle `admin` erhaelt ueber `allPermissions`
zusaetzlich `notifications.manage`.
## API
Alle Endpunkte liegen unter dem bestehenden API-Praefix `/api`.
- `GET /notifications`: eigene Benachrichtigungen, mit `page`, `pageSize` und
`status=all|read|unread`
- `GET /notifications/unread-count`: Anzahl ungelesener eigener
Benachrichtigungen
- `PATCH /notifications/:id/read`: idempotent als gelesen markieren
- `PATCH /notifications/:id/unread`: idempotent als ungelesen markieren
- `PATCH /notifications/read-all`: alle eigenen Benachrichtigungen als gelesen
markieren
- `DELETE /notifications/:id`: Soft Delete einer eigenen Benachrichtigung
- `POST /admin/notifications`: administrative Erzeugung mit
`notifications.manage`
Normale Benutzer koennen keine fremde `userId` uebergeben. Der aktuelle Benutzer
wird serverseitig aus der Session bestimmt.
## Interner Service
Andere Backend-Module erzeugen Benachrichtigungen ueber
`NotificationsService`, nicht direkt ueber ein Repository:
```ts
await notifications.createForUser({
userId,
type: NotificationType.ItemCreated,
title: 'Neuer Eintrag',
message: 'Der Eintrag "Beispiel" wurde erstellt.',
link: '/items/123',
metadata: { itemId: '123' },
});
```
Verfuegbare Methoden:
- `createForUser`
- `createForUsers`
- `getForCurrentUser`
- `getUnreadCount`
- `markAsRead`
- `markAsUnread`
- `markAllAsRead`
- `softDelete`
`createForUsers` begrenzt Bulk-Erzeugung auf 100 Zielbenutzer.
## Integrationen
`ItemsService` benachrichtigt beim Erstellen eines Items den ersten aktiven
Administrator, sofern dieser nicht der Ersteller ist. Das ist eine bewusst kleine
Beispielregel und keine vollstaendige fachliche Eskalationslogik.
`UsersService` benachrichtigt betroffene Benutzer nach erfolgreicher
Rollenaenderung. Fehler beim Erzeugen werden geloggt; die bereits erfolgreiche
sicherheitsrelevante Rollenaenderung wird dadurch nicht unkontrolliert
zurueckgerollt.
Administrative Erzeugung wird im Audit-Log als `NOTIFICATION_CREATED`
protokolliert. Geloggt werden Actor, Zielbenutzer, Notification-ID, Typ und
Request-ID, nicht die vollstaendige Nachricht oder Metadata.
## Frontend
`NotificationStore` verwendet Angular Signals fuer lokalen State und RxJS fuer
HTTP und Polling. Header-Panel und Seite `/notifications` verwenden denselben
Store, damit keine doppelten Requests fuer dieselben Daten entstehen.
Polling:
- Standardintervall: 60 Sekunden ueber `NOTIFICATION_POLL_INTERVAL_MS`
- nur bei angemeldetem Benutzer
- pausiert bei unsichtbarem Tab
- aktualisiert sofort beim Sichtbarwerden
- verhindert ueberlappende Count-Requests
- stoppt und leert State beim Logout
Links werden im Frontend nur navigiert, wenn sie interne relative Routen sind.
Titel und Nachricht werden normal interpoliert und nicht per `innerHTML`
gerendert.
## Spaetere Echtzeitkommunikation
Wenn spaeter echte Echtzeitkommunikation noetig wird, sollte das als separate
Architekturentscheidung erfolgen. Dann waeren Transport, Skalierung,
Authentifizierung, Backpressure und Betrieb gemeinsam zu entscheiden, statt
WebSockets oder Queues nebenbei in das In-App-Modul einzubauen.