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

4.8 KiB

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:

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.