Einen Aktivitäts-Feed für B2B-SaaS entwerfen: Events, Aggregation und datenschutzsicheres Logging

#Aktivitäts-Feed SaaS
Sandor Farkas - Founder & Lead Developer at Wolf-Tech

Sandor Farkas

Gründer & Lead Developer

Experte für Softwareentwicklung und Legacy-Code-Optimierung

Ein Aktivitäts-Feed klingt einfach, bis du tatsächlich einen baust. Der naive Ansatz, jedes Event zu loggen und es in umgekehrt chronologischer Reihenfolge anzuzeigen, funktioniert im Staging prima. Dann trifft Produktions-Traffic ein und du landest bei einer Wand aus Lärm: "Alice hat Feld X geändert", "Alice hat Feld Y geändert", "Alice hat Feld Z geändert", drei Einträge für ein zwei Sekunden dauerndes Formular-Speichern. Nutzer hören auf zu lesen. Support-Tickets über "verwirrende" Audit-Logs treffen ein. Und irgendwann merkt ein Security-Review an, dass du mehr personenbezogene Daten speicherst, als du in deiner Datenschutzerklärung angegeben hast.

Ein gut entworfener Aktivitäts-Feed für SaaS bedient drei Zielgruppen gleichzeitig: Endnutzer, die einen lesbaren Verlauf des Geschehenen wollen, Operations-Teams, die einen verlässlichen Audit-Trail brauchen, und Legal beziehungsweise Compliance, die genau wissen müssen, welche Daten du erhebst und warum. Diese drei auf eine Datenstruktur zu einigen ist der Großteil der Designarbeit.

Was als Event zählt

Bevor du eine einzige Zeile Code schreibst, definiere deine Event-Taxonomie. Events fallen grob in drei Kategorien:

Nutzerinitiierte Aktionen sind Dinge, die eine Person explizit getan hat: ein Projekt angelegt, einen Kollegen eingeladen, einen Plan gewechselt, eine Ressource gelöscht. Diese haben hohes Signal und sind fast immer loggenswert.

Systemausgelöste Aktionen sind Dinge, die deine Anwendung automatisch getan hat: einen geplanten Report verschickt, ein API-Token ablaufen lassen, ein regelbasiertes Label vergeben. Loggenswert, aber oft gefahrlos im nutzerseitigen Feed unterdrückbar, während sie im internen Audit-Trail bleiben.

Zustandsänderungs-Events bedeuten, dass ein Feld oder Datensatz von einem Wert zu einem anderen gewechselt ist. Das sind die lauten. Ein Nutzer, der einen Kontaktdatensatz aktualisiert, kann in unter einer Sekunde Dutzende feldbezogene Änderungs-Events erzeugen. Für Compliance sind sie relevant, aber sie ertränken einen Feed.

Diese drei Kategorien früh zu trennen macht jede nachgelagerte Entscheidung leichter: was anzeigen, was aggregieren, was speichern und was schwärzen.

Das Aggregationsproblem

Die häufigste Beschwerde über Aktivitäts-Feeds ist, dass sie zu laut sind. Die Lösung heißt Event-Aggregation, also verwandte Events zu einem lesbaren Eintrag zusammenzufassen.

Es gibt zwei Hauptstrategien:

Zeitfenster-Aggregation gruppiert Events desselben Akteurs an derselben Ressource innerhalb eines kurzen Fensters (typischerweise 30 Sekunden bis 5 Minuten). Aus "Alice hat Kontakt Acme Corp 14 Mal aktualisiert" wird "Alice hat Kontakt Acme Corp aktualisiert". Das funktioniert gut bei hochfrequenten Bearbeitungen und lässt sich mit einem Background-Job oder einem Sliding Window in Redis unkompliziert umsetzen.

Semantische Aggregation gruppiert Events nach logischer Bedeutung, unabhängig vom Timing. Wenn ein Nutzer drei Tags hinzufügt, zwei Berechtigungen entfernt und eine Beschreibung ändert, alles als Teil einer einzigen Absicht, fasst semantische Aggregation das zu "Alice hat Projekteinstellungen geändert" zusammen. Das verlangt mehr Domänenwissen, erzeugt aber einen deutlich lesbareren Feed.

In der Praxis starten die meisten Teams mit Zeitfenster-Aggregation und ergänzen semantische Regeln für bekannte hochfrequente Muster. Wichtig ist, dass die Aggregation vor dem Speichern passiert und nicht zur Query-Zeit. Aggregate beim Lesen zu berechnen skaliert nicht.

Ein Muster, das sich aus dem Event Sourcing übernehmen lässt: Speichere die Rohevents in einem Append-only-Log (deinem Audit-Trail) und schreibe eine separate materialisierte Sicht aggregierter Einträge für den Feed. Das Rohlog ist deine Source of Truth. Der Feed ist eine Projektion. Diese Trennung erlaubt dir, die Aggregationslogik zu ändern, ohne Historie zu verlieren.

Audit-Trail gegen nutzerseitigen Feed

Das sind nicht dieselben Dinge, und sie zu vermischen macht Ärger.

Der Audit-Trail dient Compliance und Betrieb. Er muss vollständig sein (kein selektives Weglassen), manipulationssicher (idealerweise append-only mit Prüfsummen), retentionsbegrenzt (du musst ihn für DSGVO-Löschanfragen bereinigen können) und maschinenlesbar. Zugriff ist typischerweise auf Admins und Security-Tools beschränkt. Niemand stöbert dort beiläufig herum.

Der nutzerseitige Feed dient Endnutzern. Er muss lesbar, relevant, schnell geladen und frei von internem Lärm sein. Er kann und sollte Systemereignisse mit geringem Signal weglassen. Er sollte natürliche Sprache verwenden, keine rohen Event-Type-Strings.

Die sauberste Architektur hält beides in getrennten Tabellen oder Collections. Das Audit-Log ist append-only ohne Soft Deletes. Der Feed ist eine denormalisierte Projektion, die du jederzeit aus dem Audit-Log neu aufbauen kannst. Wenn ein Nutzer sein Recht auf Löschung ausübt, pseudonymisierst du seine Kennung in beiden Stores, statt Zeilen zu löschen, was die Integrität des Audit-Trails zerstören würde.

Datenschutzsicheres Logging

Die DSGVO und vergleichbare Regelwerke behandeln Aktivitätslogs als personenbezogene Daten. Das hat konkrete technische Konsequenzen.

Minimiere, was du loggst. Wenn dein Feed sagt "Alice hat die Rechnungs-E-Mail auf bob@example.com geändert", hast du eine persönliche E-Mail-Adresse in deiner Aktivitätstabelle geloggt. Frage dich, ob der neue Wert überhaupt auftauchen muss. Oft reicht "Alice hat die Rechnungs-E-Mail geändert". Logge, dass eine Änderung passiert ist, nicht worauf sie geändert wurde, sofern du keinen dokumentierten Geschäftsgrund für Letzteres hast.

Pseudonymisiere Akteurs-Kennungen. Speichere im Event-Datensatz eine stabile pseudonyme ID statt der rohen Nutzer-ID. Halte die Zuordnungstabelle in einem separaten Service mit strengeren Zugriffskontrollen. Musst du später einen Nutzer löschen, änderst du die Zuordnung, statt Hunderte Event-Zeilen zu bereinigen.

Setze Aufbewahrungsfristen und setze sie durch. Die meisten SaaS-Teams einigen sich auf eine Retention-Policy, implementieren aber den Löschjob nicht. Ein wöchentlicher Cron, der Events älter als deine Policy hart löscht oder pseudonymisiert (üblich sind 12 bis 24 Monate für den nutzerseitigen Feed, länger für den Audit-Trail auf spezifischer Rechtsgrundlage), ist keine glamouröse Arbeit, aber genau das hält dich compliant.

Dokumentiere deine Rechtsgrundlage. Enterprise-Kunden werden fragen, welche personenbezogenen Daten deine Anwendung speichert und warum. Wenn dein Aktivitäts-Feed Namen, Zeitstempel und IP-Adressen loggt, sind das drei getrennte Datenkategorien, die jeweils eine dokumentierte Grundlage brauchen. Berechtigtes Interesse deckt die meisten Audit-Trail-Fälle ab, dokumentiere es einfach in deiner Datenschutzerklärung.

Schwärze sensible Felder. Zahlungsdaten, Passwörter, Security-Tokens und Gesundheitsdaten sollten nie in Event-Payloads auftauchen, auch nicht im internen Audit-Log. Baue eine feldbezogene Redaction-Liste in deinen Event-Serializer und wende sie an, bevor irgendetwas in die Datenbank gelangt.

Schema-Design

Ein minimales Event-Schema, das die meisten B2B-SaaS-Fälle abdeckt:

event_id        uuid, Primary Key
tenant_id       uuid, indiziert
actor_id        uuid (pseudonym)
actor_type      enum: user | system | api_key
resource_type   varchar: project | contact | invoice | ...
resource_id     uuid
action          varchar: created | updated | deleted | invited | ...
metadata        jsonb (Kontext ohne PII: geänderte Feldnamen, Zähler etc.)
ip_address      inet (gehasht oder je nach Policy weggelassen)
occurred_at     timestamptz, indiziert
feed_visible    boolean (false für rein interne Events)
aggregation_key varchar (null für eigenständige Events)

Die Spalte aggregation_key verdient Erwähnung. Wenn du erkennst, dass ein Event zu einer Aggregationsgruppe gehört (gleicher Akteur, gleiche Ressource, gleiches Zeitfenster), setzt du einen gemeinsamen Schlüssel. Deine Feed-Query fasst dann alle Zeilen mit gleichem Schlüssel zu einem Eintrag zusammen, nimmt den jüngsten Zeitstempel und merged die Metadaten. Das erspart einen separaten Aggregationslauf.

Für die Feed-Projektion liefert eine materialisierte Sicht oder eine separate Tabelle feed_items, die ein Background-Worker befüllt, Leselatenzen im Submillisekundenbereich. Indiziere auf (tenant_id, occurred_at DESC) und paginiere cursorbasiert (Keyset-Pagination) statt mit OFFSET. Aktivitäts-Feeds können pro Tenant Millionen Zeilen erreichen.

Den Feed ans Frontend ausliefern

Ein paar Implementierungsdetails, die oft übersehen werden:

Inkrementelles Laden über WebSockets oder SSE lohnt sich bei Echtzeit-Feeds fast immer. Alle 10 Sekunden zu pollen ist einfacher, erzeugt aber sichtbare Verzögerung und unnötige Last. Server-Sent Events sind für unidirektionale Streams betrieblich einfacher als WebSockets.

Cache aggressiv auf Tenant-Ebene. Aktivitäts-Feeds werden weit häufiger gelesen als geschrieben. Ein Redis Sorted Set mit Tenant als Key und Zeitstempel als Score liefert die letzten N Events in O(log N), ohne bei jedem Request die Datenbank zu treffen.

Trenne Lese- und Schreibpfad. Der Event-Schreibpfad muss latenzarm und hochverfügbar sein, denn ein fehlgeschlagener Event-Write während einer Nutzeraktion fühlt sich wie ein Bug an. Der Feed-Lesepfad verträgt Eventual Consistency. Entkopple sie über einen asynchronen Worker, statt in derselben Transaktion in beide Stores zu schreiben.

Den Feed mit deiner Individualsoftware verbinden

Aktivitäts-Feeds sind Infrastruktur. Sie tauchen in fast jedem Custom Software Development-Projekt ab einer gewissen Komplexitätsschwelle auf, und sie sind im ursprünglichen Briefing meist unterspezifiziert, weil sie von außen einfach aussehen.

Die Teams mit den wartbarsten Implementierungen sind die, die Audit-Log und Feed vom ersten Tag an als vollwertige Domänenthemen behandeln und nicht als etwas, das nach dem Launch der Kernfeatures angeflanscht wird. Sobald du hunderttausend Zeilen uneinheitlich strukturierter Events hast, ist das Nachrüsten von Aggregation und Datenschutzkontrollen schmerzhaft.

Wenn deine Anwendung im frühen Wachstum steckt und der Aktivitäts-Feed zur Wartungslast wird, oder wenn Compliance-Fragen zur Datenaufbewahrung von Enterprise-Interessenten eintreffen, ist das ein guter Moment, einen Schritt zurückzutreten und die Datenschicht neu zu entwerfen. Das ist erheblich günstiger, als es nach einem Datenschutz-Audit zu tun.

Eine Anmerkung zu Drittanbieter-Lösungen

Bibliotheken wie logidze (Change-Tracking auf PostgreSQL-Ebene) und Dienste wie Inngest oder Temporal decken Teile dieses Problems ab. Sie sind eine Evaluierung wert, besonders bei kleinem Team. Die Abwägungen: Change-Capture-Tools von Drittanbietern liefern Vollständigkeit auf Kosten der Kontrolle über das Schema, Workflow-Engines lösen das Durability-Problem, bringen aber Betriebsaufwand mit.

Für die meisten B2B-SaaS-Anwendungen in der Größenordnung von 10.000 bis 100.000 Nutzern ist eine gut entworfene eigene Event-Tabelle mit einem Background-Aggregationsworker die richtige Wahl. Sie hält das Datenmodell transparent, macht Compliance leichter nachvollziehbar und vermeidet, deinen Audit-Trail an die Retention-Policy eines Anbieters zu koppeln.

Zusammenfassung

Ein guter B2B-SaaS-Aktivitäts-Feed ruht auf vier Entscheidungen: einer klaren Event-Taxonomie (nutzerinitiiert gegen System gegen Zustandsänderung), einer vor dem Speichern angewandten Aggregationsstrategie, einer harten Trennung zwischen Audit-Trail und nutzerseitigem Feed und einem Datenschutzmodell, das gespeicherte personenbezogene Daten von Anfang an minimiert. Bekommst du die richtig hin, ist die Umsetzung geradlinig. Überspringst du sie, rüstest du später alle vier unter Druck nach.

Wenn du im Rahmen eines größeren Web Application Development-Vorhabens einen Aktivitäts-Feed entwirfst oder neu architektierst und eine zweite Meinung zum Datenmodell oder Datenschutzansatz willst, melde dich unter hello@wolf-tech.io oder besuche wolf-tech.io.


FAQ

Wie lange sollte ich Aktivitäts-Feed-Daten aufbewahren? Für den nutzerseitigen Feed sind 12 Monate ein üblicher Standard, Enterprise-Kunden verlangen mitunter 24 Monate. Für den internen Audit-Trail bestimmt deine Rechtsgrundlage die Grenze, bei Finanzereignissen sind es nach lokalen Vorgaben oft 3 bis 7 Jahre. Was immer du festlegst, automatisiere den Lösch- oder Pseudonymisierungsjob und teste ihn.

Soll ich alte und neue Werte geänderter Felder loggen? Nur mit dokumentiertem Grund. Änderungs-Diffs zu loggen ist nützlich für Undo-Funktionen oder Compliance in regulierten Branchen. Es erhöht aber auch deutlich die Menge gespeicherter personenbezogener Daten. Wenn du Diffs loggst, wende feldbezogene Schwärzung auf alles Sensible an und lege Diffs in einer separaten Tabelle mit strengerer Aufbewahrungsfrist als das Haupt-Event-Log ab.

Kann ich eine bestehende Bibliothek nutzen, statt selbst zu bauen? Ja, besonders für die Audit-Trail-Schicht. Lösungen auf PostgreSQL-Ebene wie logidze oder temporal_tables erledigen Change Capture automatisch. Für den nutzerseitigen Feed wirst du fast sicher eigene Aggregationslogik brauchen, kannst sie aber auf dem aufbauen, was die Bibliothek erfasst.