Blog
Architecture
6. Oktober 20258 min

3 PostgreSQL-Schemas zur sauberen Strukturierung von Supabase

Wichtige Punkte: Eine Supabase PostgreSQL-Datenbank von Anfang an in drei separate Schemata (privat, collaborative, gamification) aufzuteilen, verhindert Chaos, wenn das Projekt wächst. Dieser Artikel behandelt das Warum, das Wie, RLS-Richtlinien, Namenskonventionen und Fehler, die man vermeiden sollte.

Wenn du ein Projekt startest, ist die Versuchung groß: Du erstellst deine Tabellen im public-Schema von PostgreSQL und legst los. Eine Tabelle hier, eine andere dort. Nach drei Wochen hast du 40 Tabellen, die ohne jegliche Logik vermischt sind, inkonsistente Namen haben und du hast panische Angst, irgendetwas anzufassen.

Ich habe mich sehr früh in der Entwicklung von TAMSIV entschieden, die Datenbank mit drei separaten Schemata zu strukturieren. Sechs Monate und über 650 Commits später ist dies wahrscheinlich die beste architektonische Entscheidung, die ich getroffen habe. Hier ist der Grund dafür und wie man es konkret mit Supabase umsetzt.

Organisiertes Ablagesystem mit farbigen Ordnern in einem modernen Büro
Drei Schemata, wie drei Aktenschränke: jedes hat seinen Bereich.

Warum nicht alles ins Public-Schema packen?

Das public-Schema von PostgreSQL ist die Standardeinstellung. Supabase verwendet es für seine eigenen internen Tabellen. Wenn du deine Tabellen hinzufügst, vermischst du deine Geschäftsdaten mit der Infrastruktur. Das ist wie Kleidung in der Küche aufzubewahren – es funktioniert, aber es ist Chaos.

Die konkreten Probleme, die ich identifiziert habe:

  • Lesbarkeit: Mit über 40 Tabellen in einem einzigen Schema wird das Auffinden einer Tabelle zu einem Ratespiel.
  • Sicherheit: RLS-Richtlinien (Row Level Security) werden unmöglich zu begründen, wenn Tabellen aus verschiedenen Domänen koexistieren.
  • Evolution: Das Hinzufügen einer neuen funktionalen Domäne (Gamification, Analytics) ohne das Bestehende zu berühren, ist unmöglich, wenn alles im selben Topf ist.
  • Zusammenarbeit: Wenn ein anderer Entwickler (oder du selbst in 6 Monaten) das Projekt entdeckt, weiß er nicht, wo er anfangen soll.

Wie strukturiert man die Schemata einer Supabase-App?

Hier sind die drei Schemata von TAMSIV und was sie enthalten:

Schema privat — die persönlichen Daten

Alles, was einem Benutzer gehört und nur ihm allein:

  • privat.tasks — Persönliche Aufgaben
  • privat.memos — Sprach- und Textmemos
  • privat.calendar_events — Kalenderereignisse
  • privat.user_profiles — Benutzerprofile
  • privat.task_attachments / privat.memo_attachments — Anhänge

Warum privat und nicht private? Weil private ein reserviertes Wort in SQL ist. Das habe ich auf die harte Tour gelernt, nach einer fehlgeschlagenen Migration, die das gesamte Schema zerstört hat. Die Fehlermeldung war nicht einmal klar – es dauerte 45 Minuten, um zu verstehen, dass der Schemaname das Problem war.

Schema collaborative — die Gruppen

Alles, was mit Teamarbeit zu tun hat:

  • collaborative.groups — Hierarchische Gruppen (bis zu 6 Ebenen)
  • collaborative.group_members — Mitglieder und Rollen
  • collaborative.group_tasks / collaborative.group_memos — Geteilte Inhalte
  • collaborative.checklists — Checklisten mit Validierung

Dieses Schema ist das komplexeste in Bezug auf RLS-Richtlinien, da die Zugriffsregeln von der Rolle des Benutzers in der Gruppe, der Gruppenhierarchie und den geerbten Berechtigungen abhängen. Ich habe die Komplexität hierarchischer Gruppen im speziellen Artikel detailliert beschrieben.

Schema gamification — das Engagement

Alles, was die App süchtig macht (im positiven Sinne):

  • gamification.user_stats — Punkte, Level, aktuelle Serie
  • gamification.user_badges — Freischaltbare Abzeichen
  • gamification.points_history — Detaillierte Punktehistorie
  • gamification.daily_challenges — Tägliche Herausforderungen
  • gamification.feed_activity — Sozialer Aktivitäts-Feed

Dieses Schema wurde drei Monate nach Projektbeginn hinzugefügt. Dank der Trennung habe ich ein neues Schema erstellt, ohne die beiden anderen zu berühren. Null Risiko, das Bestehende zu zerstören. Die Gamification-Architektur wird im Artikel über das Gamification-Schema detailliert beschrieben.

Whiteboard mit Datenbank-Schema-Diagramm, gezeichnet mit blauen und grünen Markern
Die Schema-Planung auf dem Whiteboard, bevor eine einzige Zeile SQL angefasst wird.

Welche konkreten Vorteile ergeben sich in der Praxis?

Über die Theorie hinaus, hier ist, was mir die Schema-Trennung im Alltag gebracht hat:

Sofortige Lesbarkeit

Wenn ich SELECT * FROM privat.tasks mache, weiß ich sofort, dass es sich um persönliche Daten handelt. Wenn ich collaborative.group_members sehe, weiß ich, dass es mit Gruppen zusammenhängt. Keine zusätzliche Dokumentation nötig – das Schema IST die Dokumentation.

Einfachere RLS-Richtlinien zu begründen

Die Regeln des privat-Schemas sind glasklar: Du siehst nur deine Daten. Punkt. Die RLS-Richtlinie besteht aus einer Zeile:

CREATE POLICY "users_own_data" ON privat.tasks
  USING (user_id = auth.uid());

Die des collaborative-Schemas sind komplexer: Du siehst die Daten der Gruppen, denen du angehörst, entsprechend deiner Rolle, mit Vererbung der übergeordneten Berechtigungen. Aber da sie in ihrem Schema isoliert sind, breitet sich die Komplexität nicht auf den Rest aus.

Risikofreie Evolution

Das Hinzufügen der Gamification erforderte keine Änderung der bestehenden Schemata. Auch das Hinzufügen des Kalenders mit Teilnehmern und Filtern nicht. Jede neue funktionale Domäne ist autonom.

Wie verwaltet man RLS-Richtlinien mit mehreren Schemata?

Supabase verwendet Row Level Security (RLS), um den Datenzugriff zu sichern. Das Prinzip ist einfach: Jede Tabelle hat Regeln, die bestimmen, wer lesen, schreiben, ändern, löschen darf.

In der Praxis ist es ein Labyrinth. Insgesamt hat TAMSIV mehr als 30 RLS-Richtlinien, jede einzeln getestet. So organisiere ich sie:

  • Schema privat: Einfache Richtlinien, basierend auf auth.uid() = user_id.
  • Schema collaborative: Komplexe Richtlinien, die die Gruppenzugehörigkeit UND die Rolle überprüfen. Verwendung von Unterabfragen auf collaborative.group_members.
  • Schema gamification: Gemischte Richtlinien – öffentliches Lesen für das Leaderboard, Schreiben nur über RPC-Serverfunktionen.

Die häufigste Falle: eine zu permissive RLS-Richtlinie, die Daten zwischen Benutzern durchsickern lässt. Ich spreche darüber im Artikel über Sicherheitsaudit und Rate Limiting.

Welche Namenskonventionen sollte man anwenden?

Strenge Konventionen vom ersten Tag an vermeiden Stunden der Verwirrung später. Hier sind die von TAMSIV:

  • Tabellen: snake_case im Plural (tasks, group_members, daily_challenges)
  • Spalten: snake_case (user_id, created_at, is_completed)
  • RPC-Parameter: Präfix p_ (p_start_date, p_group_id, p_user_id)
  • RPC-Funktionen: Verb_Objekt (get_consolidated_feed, add_gamification_points)

Das Präfix p_ für RPC-Parameter ist entscheidend. Ohne es, wenn dein Parameter user_id mit der Spalte user_id in einer Abfrage kollidiert, löst PostgreSQL keinen Fehler aus – es nimmt die Spalte als Priorität. Das Ergebnis: eine Abfrage, die unerwartete Daten zurückgibt, ein stiller und gefährlicher Fehler.

Wie migriert man zu mehreren Schemata, wenn das Projekt bereits existiert?

Wenn du bereits alles in public hast und migrieren möchtest, ist hier die Strategie:

  1. Audit: Liste alle deine Tabellen auf und klassifiziere sie nach funktionaler Domäne.
  2. Erstelle die Schemata: CREATE SCHEMA privat; CREATE SCHEMA collaborative;
  3. Migriere Tabelle für Tabelle: ALTER TABLE public.tasks SET SCHEMA privat;
  4. Aktualisiere die RLS-Richtlinien: Sie sind an die Tabelle gebunden, also folgen sie der Verschiebung.
  5. Aktualisiere den Frontend-Code: Alle Supabase-Abfragen müssen jetzt das Schema angeben.

Achtung: Fremdschlüssel, Trigger und RPC-Funktionen müssen manuell aktualisiert werden. Das ist eine große Aufgabe, aber der Gewinn an Wartbarkeit ist enorm. Wenn du Supabase verwendest, lies auch meinen Artikel zur Reduzierung des Egress, um die Kosten zu optimieren.

Sicherheitsschloss auf einer Laptop-Tastatur mit blauer Beleuchtung
Datensicherheit beginnt mit einer guten Datenbankarchitektur.

Welche Fehler sollte man bei der Strukturierung vermeiden?

In 6 Monaten Solo-Entwicklung habe ich mehrere Fallstricke identifiziert:

  • Verwende keine SQL-reservierten Wörter als Schemanamen. private, public, user sind alle reserviert. Verwende Alternativen (privat, app_public, accounts).
  • Vergiss nicht die GRANT-Berechtigungen. Ein Schema zu erstellen reicht nicht aus – du musst den Supabase-Rollen (anon, authenticated, service_role) explizit Zugriffsrechte erteilen.
  • Vertraue niemals lokalen Migrationsdateien. Die reale Datenbank ist die einzige Quelle der Wahrheit. Migrationsdateien können nach manuellen Korrekturen abweichen.
  • Teste jede RLS-Richtlinie einzeln. Über 30 Richtlinien bedeuten mindestens 30 Testszenarien. Das ist methodisch, langweilig und absolut unerlässlich.

Welche Auswirkungen hat das auf die Leistung?

Gute Nachrichten: PostgreSQL-Schemata haben keine Auswirkungen auf die Abfrageleistung. Ein SELECT FROM privat.tasks ist genau so schnell wie ein SELECT FROM public.tasks. Schemata sind eine logische, keine physische Organisation.

Die Trennung erleichtert jedoch die Optimierung. Du kannst spezifische Indizes pro Domäne hinzufügen, verschiedene Vacuum-Parameter pro Schema konfigurieren und die Leistung pro funktionaler Domäne überwachen.

Für das Frontend spielte das Refactoring der Clean Architecture auch eine entscheidende Rolle bei der Wartbarkeit des Codes, der mit der Datenbank interagiert.

FAQ

Wie viele Schemata sollte man für eine klassische App erstellen?

Zwei bis vier reichen für die meisten Projekte aus. Eines für Benutzerdaten, eines für gemeinsame/kollaborative Funktionen, eventuell eines für Analysen oder Gamification. Wichtig ist die Trennung nach funktionaler Domäne, nicht nach Datentyp.

Unterstützt Supabase mehrere Schemata gut?

Ja, aber du musst die Berechtigungen manuell konfigurieren. Standardmäßig ist nur das public-Schema über die REST-API zugänglich. Um ein anderes Schema freizugeben, musst du es in den Supabase-Projekteinstellungen hinzufügen und die entsprechenden GRANT-Berechtigungen für die Rollen anon und authenticated konfigurieren.

Braucht man Fremdschlüssel zwischen Schemata?

Ja, und das wird von PostgreSQL perfekt unterstützt. Eine Tabelle in collaborative kann auf eine Tabelle in privat verweisen. Fremdschlüsselbeschränkungen funktionieren zwischen Schemata ohne Einschränkungen.

Wie verwaltet man RPC-Funktionen, die mehrere Schemata betreffen?

RPC-Funktionen (wie get_consolidated_feed) können problemlos Tabellen aus verschiedenen Schemata verknüpfen. Die bewährte Methode ist, die Funktion im Schema der Hauptdomäne zu erstellen, der sie dient, und vollqualifizierte Namen (privat.tasks) in den Abfragen zu verwenden.

Kann man nach der Migration zu mehreren Schemata wieder zurück?

Technisch ja, mit ALTER TABLE SET SCHEMA public. Aber in der Praxis, sobald der Frontend-Code die Schemata referenziert, ist die Rückkehr kostspielig. Deshalb ist es besser, diese Entscheidung frühzeitig im Projekt zu treffen.