Blog
Architecture
12. Oktober 20258 min

Hierarchische Gruppen mit 6 Ebenen: Die komplexeste Funktion

Kernpunkte: Der Aufbau eines hierarchischen Gruppensystems mit 6 Ebenen Tiefe und 4 Rollen, rekursiven PostgreSQL-Abfragen (CTE) und 31 RLS-Richtlinien ist die komplexeste Funktion von TAMSIV. Dieser Artikel behandelt das Datenmodell, die Vererbung von Berechtigungen, die Frontend-Komponente HierarchicalGroupPicker und die gewonnenen Erkenntnisse.

Es gibt Funktionen, die auf dem Papier einfach erscheinen. "Füge Gruppen hinzu" — das klingt harmlos. Wie das Hinzufügen eines Warenkorbs in einer E-Commerce-App. Dann fängst du an zu graben und stellst fest, dass du gerade die Büchse der Pandora geöffnet hast.

TAMSIV unterstützt hierarchische Gruppen mit bis zu 6 Ebenen Tiefe. Mein Tauchclub war der perfekte Anwendungsfall: Club → Technische Kommission → Stufe 1 → Dienstagsgruppe. Eine Familie nutzt: Familie → Haus → Küche / Garten / Garage. Ein KMU: Unternehmen → Abteilung → Team → Projekt.

Dies ist die Funktion, die mich am meisten Zeit gekostet hat. Es ist auch diejenige, die TAMSIV für echte Organisationen nutzbar macht, nicht nur für den Solo-Gebrauch.

Team-Meeting an einem modernen Konferenztisch mit Laptops, warmer Beleuchtung
Kollaborative Gruppen verwandeln eine persönliche App in ein Team-Tool.

Wie modelliert man hierarchische Gruppen in PostgreSQL?

Das Datenmodell basiert auf einem einfachen Konzept: Jede Gruppe hat eine parent_id, die auf ihre übergeordnete Gruppe verweist. Eine Stammgruppe hat parent_id = NULL.

-- Vereinfachte Tabelle
CREATE TABLE collaborative.groups (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  name TEXT NOT NULL,
  parent_id UUID REFERENCES collaborative.groups(id),
  created_by UUID REFERENCES auth.users(id),
  depth INTEGER DEFAULT 0,
  created_at TIMESTAMPTZ DEFAULT now()
);

Um den vollständigen Baum einer Gruppe anzuzeigen, verwende ich rekursive Abfragen (Common Table Expressions, oder CTE) in PostgreSQL:

WITH RECURSIVE group_tree AS (
  -- Basis: die Stammgruppe
  SELECT id, name, parent_id, 0 AS depth
  FROM collaborative.groups
  WHERE id = p_group_id

  UNION ALL

  -- Rekursion: die Untergruppen
  SELECT g.id, g.name, g.parent_id, gt.depth + 1
  FROM collaborative.groups g
  JOIN group_tree gt ON g.parent_id = gt.id
  WHERE gt.depth < 6  -- Schutzmechanismus
)
SELECT * FROM group_tree;

Die Klausel WHERE depth < 6 ist ein kritischer Schutzmechanismus. Ohne sie würde ein Datenfehler (z.B. ein Referenzzyklus) die Abfrage in eine Endlosschleife schicken. Sechs Ebenen decken alle realen Anwendungsfälle ab, die ich bisher gesehen habe.

Dieses Datenmodell befindet sich im Schema collaborative, getrennt vom Rest — ich erkläre warum im Artikel über die Datenbankstrukturierung.

Was sind die vier Rollen und wie funktionieren sie?

Jedes Mitglied einer Gruppe hat eine Rolle, die seine Berechtigungen bestimmt:

  • Admin — Alles tun: erstellen, ändern, löschen, Mitglieder verwalten, einladen, Gruppeneinstellungen ändern. Dies ist standardmäßig der Gruppenersteller.
  • Manager — Inhalte verwalten: Aufgaben und Memos erstellen/ändern/löschen, Mitglieder zuweisen, Checklisten validieren. Aber keine Mitglieder oder Einstellungen verwalten.
  • Member — Beitragen: Inhalte erstellen, alle Gruppeninhalte sehen, eigene Checklistenpunkte validieren.
  • Viewer — Nur Lesezugriff: Inhalte sehen, sonst nichts. Nützlich für Beobachter oder Stakeholder, die den Fortschritt verfolgen möchten, ohne einzugreifen.

Die Falle der Berechtigungsvererbung

Wenn du Admin der Gruppe "Tauchclub" bist, bist du dann automatisch Admin der "Technischen Kommission" (einer Untergruppe)? In TAMSIV lautet die Antwort ja — aber mit Nuancen.

Die Vererbung funktioniert nach unten: Ein Admin einer übergeordneten Gruppe hat die gleichen Rechte in allen Untergruppen. Aber ein Admin einer Untergruppe hat keine Rechte in der übergeordneten Gruppe. Diese Asymmetrie ist natürlich (der Direktor sieht alles, der Teamleiter sieht sein Team), aber sie erhöht die Komplexität der SQL-Abfragen.

Um Berechtigungen zu überprüfen, muss jede Abfrage den hierarchischen Baum hinaufgehen, bis eine Rolle gefunden oder die Wurzel erreicht wird:

-- Hat der Benutzer eine Rolle in dieser Gruppe oder einem Elternteil?
WITH RECURSIVE parent_chain AS (
  SELECT id, parent_id FROM collaborative.groups WHERE id = p_group_id
  UNION ALL
  SELECT g.id, g.parent_id
  FROM collaborative.groups g
  JOIN parent_chain pc ON g.id = pc.parent_id
)
SELECT role FROM collaborative.group_members
WHERE group_id IN (SELECT id FROM parent_chain)
AND user_id = auth.uid()
ORDER BY role ASC  -- Admin > Manager > Member > Viewer
LIMIT 1;
Organigramm an einer Korktafel mit bunten Reißnägeln und Verbindungslinien
Jede Ebene der Hierarchie hat ihre eigenen Zugriffsregeln.

Wie schreibt und testet man 31 RLS-Richtlinien?

Das ist die schmerzhafte Zahl: 31 Row Level Security-Richtlinien für das Schema collaborative. Supabase verwendet RLS, um den Datenzugriff zu sichern — jede Tabelle hat Regeln, die bestimmen, wer lesen, schreiben, ändern, löschen kann.

Hier sind konkrete Beispiele:

Einfache Lesepolitik

-- Ein Mitglied kann die Aufgaben seiner Gruppen sehen
CREATE POLICY "members_read_tasks" ON collaborative.group_tasks
  FOR SELECT USING (
    EXISTS (
      SELECT 1 FROM collaborative.group_members gm
      WHERE gm.group_id = group_tasks.group_id
      AND gm.user_id = auth.uid()
    )
  );

Politik mit hierarchischer Vererbung

-- Ein Admin kann die Aufgaben seiner Gruppen UND Untergruppen löschen
CREATE POLICY "admin_delete_tasks" ON collaborative.group_tasks
  FOR DELETE USING (
    EXISTS (
      WITH RECURSIVE parent_chain AS (
        SELECT id, parent_id FROM collaborative.groups
        WHERE id = group_tasks.group_id
        UNION ALL
        SELECT g.id, g.parent_id
        FROM collaborative.groups g
        JOIN parent_chain pc ON g.id = pc.parent_id
      )
      SELECT 1 FROM collaborative.group_members gm
      WHERE gm.group_id IN (SELECT id FROM parent_chain)
      AND gm.user_id = auth.uid()
      AND gm.role = 'admin'
    )
  );

31 Richtlinien zu testen, das sind mindestens 31 Testszenarien. In Wirklichkeit sind es viel mehr, denn jede Richtlinie muss positiv (Zugriff wird gewährt) UND negativ (Zugriff wird verweigert) getestet werden. Methodisch, langweilig, unerlässlich. Details zum Testansatz findest du im Artikel über das Sicherheitsaudit.

Wie baut man die Komponente HierarchicalGroupPicker?

Auf der Frontend-Seite ist die Anzeige eines Gruppenbaums mit Einrückung, Rollensymbolen und Interaktionen eine eigenständige UI-Herausforderung.

Der HierarchicalGroupPicker ist eine React Native-Komponente, die:

  • Den Baum anzeigt mit Einrückung proportional zur Tiefe (unter Verwendung von spacing() des Skalierungssystems).
  • Die Rolle des Benutzers in jeder Gruppe über ein farbiges Symbol anzeigt.
  • Die Auswahl ermöglicht mit einem Umschalter "Untergruppen einschließen" — wenn du eine übergeordnete Gruppe auswählst, können die Untergruppen automatisch eingeschlossen werden.
  • Das Ein-/Ausklappen unterstützt: Untergruppen können eingeklappt werden, um den Bildschirm bei tiefen Hierarchien nicht zu überladen.

Diese Komponente wird überall wiederverwendet: im Agenda-Filter, bei der Aufgabenerstellung, im Gruppenverwaltungsbildschirm. Es ist anfangs eine enorme Zeitinvestition, aber danach eine kolossale Zeitersparnis.

Das FilterBar-Muster

Der HierarchicalGroupPicker arbeitet mit der FilterBar zusammen, einer Filterkomponente mit 3 Kontexten (Privat / Geteilt / Alle) und Modi (alle Aufgaben / von mir erstellt / mir zugewiesen). Die Kombination der beiden ermöglicht sehr präzise Abfragen: "Zeige mir die mir zugewiesenen Aufgaben in der Gruppe Familie und ihren Untergruppen".

Matroschka-Puppen, von der größten zur kleinsten, auf einem Holzregal aufgereiht
Gruppen in Gruppen in Gruppen — wie russische Puppen.

Was sind die realen Anwendungsfälle für hierarchische Gruppen?

Hier sind die Anwendungsfälle, die meine Tester aktiv nutzen:

  • Familie: Familie → Haus (Haushaltsaufgaben) / Urlaub (Planung) / Schule (Hausaufgaben der Kinder). Ich spreche darüber im Artikel über Familienorganisation.
  • Sportverein: Verein → Kommissionen → Stufen → Gruppen nach Zeitfenster.
  • Kleines Team: Unternehmen → Abteilung → Projekt → Sprint.
  • Verein: Verein → Bereich → Veranstaltung.

Die Grenze von 6 Ebenen wird selten erreicht. In der Praxis decken 3-4 Ebenen 95% der Anforderungen ab. Aber diese maximale Tiefe ist ein Sicherheitsnetz — besser haben und nicht brauchen.

Welche Lehren sind aus dieser Implementierung zu ziehen?

  1. Rekursive PostgreSQL-Abfragen sind mächtig, aber kostspielig: Jede Abfrage, die die Hierarchie hinaufgeht, führt N Joins aus (wobei N = die Tiefe). Ein Index auf parent_id ist unerlässlich.
  2. RLS-Richtlinien häufen sich schnell an: Jede Tabelle × jede Operation (SELECT, INSERT, UPDATE, DELETE) × jede Rolle = viele Richtlinien. Dokumentiere sie in einer dedizierten Datei.
  3. Die Vererbung von Berechtigungen ist der heikelste Punkt: Entscheide frühzeitig, ob sich Berechtigungen nach unten, nach oben oder in beide Richtungen ausbreiten. Eine spätere Änderung dieser Entscheidung ist extrem kostspielig.
  4. Die Frontend-Komponente ist eine Investition: Ein guter HierarchicalGroupPicker dauert eine Woche, um richtig gebaut zu werden, wird aber überall wiederverwendet.
  5. Teste mit realen Daten: Ein Baum von 3 Gruppen in der Entwicklung deckt nicht die gleichen Fehler auf wie ein Baum von 15 Gruppen auf 4 Ebenen in der Produktion.

Wenn du ein kollaboratives System aufbaust, ist die Hierarchie das, was eine einfache "Gruppenliste" in ein Werkzeug verwandelt, das Organisationen wirklich annehmen. Es ist hart, es ist langwierig, aber es ist das, was den Unterschied zu Konkurrenten ausmacht, die sich auf flache Gruppen beschränken.

FAQ

Warum auf 6 Ebenen Tiefe beschränken?

Sechs Ebenen decken alle realen Anwendungsfälle ab, die ich bisher gesehen habe. Darüber hinaus wird der Baum für den Benutzer unübersichtlich. Die Grenze schützt auch vor zu tiefen rekursiven Abfragen, die die Leistung beeinträchtigen könnten. In der Praxis reichen 3-4 Ebenen für 95% der Organisationen aus.

Verursachen rekursive Abfragen Leistungsprobleme?

Mit einem Index auf parent_id und einer Tiefenbegrenzung ist die Leistung auch bei Hunderten von Gruppen ausgezeichnet. Eine rekursive Abfrage über 6 Ebenen dauert auf Supabase PostgreSQL weniger als 10 ms. Das eigentliche Risiko ist ein Referenzzyklus (Gruppe A Elternteil von B, B Elternteil von A) — die Klausel WHERE depth < 6 schützt davor.

Wie verwaltet man die Einladung neuer Mitglieder?

Der Admin einer Gruppe kann per E-Mail oder über einen Freigabelink einladen. Der Eingeladene tritt der Gruppe mit der vom Admin gewählten Rolle bei. Wenn die Gruppe Untergruppen hat, hat der Eingeladene nur Zugriff auf die Gruppe, in die er eingeladen wurde — es sei denn, ein Admin des Elternteils gewährt ihm explizit Zugriff.

Funktionieren Gruppen-Checklisten anders als persönliche Checklisten?

Ja. Gruppen-Checklisten haben zwei Validierungsmodi: "single" (eine Person validiert für alle) und "everyone" (jedes Mitglied muss einzeln validieren). Der Modus "everyone" ist perfekt für Familien-Einkaufslisten, bei denen niemand dasselbe kauft.

Kann man eine Gruppe in eine andere übergeordnete Gruppe verschieben?

Ja, ein Admin kann die Hierarchie neu organisieren, indem er die parent_id einer Gruppe ändert. Dies ist jedoch ein sensibler Vorgang: Die vererbten Berechtigungen ändern sich sofort, und die Mitglieder des neuen Elternteils sehen den Inhalt der verschobenen Gruppe. Eine explizite Bestätigungsnachricht warnt den Admin vor den Konsequenzen.