Multi-Tenant-SaaS-Datenmodelloptionen für ein No-Code-Backend
Auswahl des Multi-Tenant-SaaS-Datenmodells beeinflusst Sicherheit, Reporting und Performance. Vergleiche tenant_id, separate Schemas und separate Datenbanken mit klaren Kompromissen.

Das Problem: Mandanten trennen, ohne zu verlangsamen
Multi-Tenancy bedeutet, dass ein Softwareprodukt viele Kunden (Mandanten) bedient, und jeder Mandant darf nur seine eigenen Daten sehen. Die Schwierigkeit ist, das konsequent umzusetzen: nicht nur auf einem Bildschirm, sondern bei jedem API-Aufruf, im Admin-Panel, beim Export und bei Hintergrundjobs.
Dein Datenmodell beeinflusst den Alltag stärker als viele Teams erwarten. Es formt Berechtigungen, Reporting, Abfragegeschwindigkeit mit wachsender Nutzung und wie riskant ein „kleiner“ Bug werden kann. Vergisst du einen Filter, kannst du Daten leaken. Isolierst du zu stark, wird Reporting zur Qual.
Es gibt drei gängige Wege, ein Multi-Tenant-SaaS-Datenmodell aufzubauen:
- Eine Datenbank, in der jede Tabelle
tenant_identhält - Separate Schemas pro Mandant in einer Datenbank
- Separate Datenbanken pro Mandant
Selbst wenn du visuell in einem No-Code-Backend baust, gelten dieselben Kompromisse. Tools wie AppMaster erzeugen aus deinem Entwurf echten Backend-Code und Datenbankstrukturen, sodass frühe Modellentscheidungen schnell in Produktivverhalten und Performance sichtbar werden.
Stell dir ein Helpdesk-Tool vor. Wenn jede Ticket-Zeile tenant_id hat, ist es einfach, „alle offenen Tickets“ abzufragen, aber du musst Mandantenchecks überall erzwingen. Haben Mandanten eigene Schemas oder Datenbanken, ist die Isolation standardmäßig stärker, aber Cross-Tenant-Reporting (z. B. „durchschnittliche Schließzeit über alle Kunden“) wird aufwändiger.
Das Ziel ist eine Trennung, der du vertrauen kannst, ohne Reporting, Support und Wachstum unnötig zu erschweren.
Schnellwahl: 4 Fragen, die einschränken
Fang nicht mit Datenbanktheorie an. Fang an mit der Nutzung des Produkts und dem, was du wöchentlich operativ brauchst.
Vier Fragen, die die Antwort meist offensichtlich machen
-
Wie sensibel sind die Daten und stehst du unter strengen Regeln? Gesundheit, Finanzen und enge Kundenverträge treiben oft in Richtung stärkere Isolation (separates Schema oder separate DB). Das kann Risiko reduzieren und Audits erleichtern.
-
Braucht ihr häufig Cross-Tenant-Reporting? Wenn du regelmäßig „alle Kunden“-Metriken (Nutzung, Umsatz, Performance) brauchst, ist eine einzelne DB mit
tenant_idmeist am einfachsten. Separate DBs machen das schwerer, weil du viele Orte abfragen und Ergebnisse kombinieren musst. -
Wie unterschiedlich werden Mandanten zueinander sein? Wenn Mandanten benutzerdefinierte Felder, Workflows oder Integrationen benötigen, verringern separate Schemas oder DBs das Risiko, dass Änderungen überschwappen. Wenn die meisten Mandanten dieselbe Struktur teilen, bleibt
tenant_idsauber. -
Was kann dein Team realistisch betreiben? Mehr Isolation bedeutet meist mehr Arbeit: mehr Backups, mehr Migrationen, mehr Komponenten und mehr Orte, an denen Fehler sich verstecken.
Ein praktischer Ansatz ist, die zwei besten Optionen zu prototypen und die echten Schmerzpunkte zu testen: Berechtigungsregeln, Reporting-Abfragen und wie Änderungen ausgerollt werden, während das Modell sich entwickelt.
Ansatz 1: Eine Datenbank mit tenant_id in jeder Zeile
Das ist die häufigste Konstellation: alle Kunden teilen dieselben Tabellen, und jeder mandantenbezogene Datensatz trägt eine tenant_id. Operativ ist das einfach, weil du eine Datenbank und ein Migration-Set verwaltest.
Die Regel ist strikt: gehört eine Zeile zu einem Mandanten, muss sie tenant_id enthalten und jede Abfrage muss danach filtern. Mandanten-eigene Tabellen sind typischerweise users, roles, projects, tickets, invoices, messages, Dateimetadaten und Join-Tabellen, die Mandantendaten verbinden.
Um Leaks zu reduzieren, behandle tenant_id als nicht verhandelbar:
- Mach
tenant_iderforderlich (NOT NULL) in mandanten-eigenen Tabellen - Füge Indexe hinzu, die mit
tenant_idbeginnen (z. B.tenant_id, created_at) - Mache Unique-Regeln mandantenspezifisch (z. B. E-Mail eindeutig pro Mandant)
- Übergib
tenant_iddurch jede API- und Business-Flow, nicht nur durch UI-Formulare - Erzwinge es im Backend, nicht nur durch Client-Filter
In PostgreSQL können Row-Level-Security-Policies eine starke Sicherheitsnetzfunktion bieten, besonders wenn Abfragen dynamisch generiert werden.
Referenzdaten fallen meist in zwei Gruppen: Shared-Tabellen (z. B. countries) ohne tenant_id und mandantenspezifische Kataloge (z. B. custom tags oder pipelines) mit tenant_id.
Wenn du mit AppMaster baust, verhindert eine einfache Gewohnheit die meisten Zwischenfälle: setze tenant_id aus dem authentifizierten Mandanten des Nutzers, bevor du in deiner Business-Process-Logik erstellst oder liest, und halte dieses Muster konsistent.
Einfluss auf Berechtigungen: was sich mit jedem Ansatz ändert
Berechtigungen sind der Punkt, an dem Multi-Tenancy gelingt oder scheitert. Das gewählte Datenlayout ändert, wie du Nutzer speicherst, wie Abfragen eingegrenzt werden und wie du „Ups“ in Admin-Ansichten vermeidest.
Bei einer Single-DB mit tenant_id nutzen Teams oft eine gemeinsame Users-Tabelle und verbinden jeden Nutzer mit einem Mandanten und einer oder mehreren Rollen. Die große Regel bleibt: jedes Lesen und Schreiben muss Mandantenkontext enthalten, selbst bei „kleinen“ Tabellen wie Einstellungen, Tags oder Logs.
Bei separaten Schemas behältst du oft eine gemeinsame Identitätsschicht (Login, Passwort, MFA) an einem Ort, während Mandantendaten in einem Schema pro Mandant liegen. Berechtigungen werden teilweise zu einem Routing-Problem: die App muss vor der Business-Logik auf das richtige Schema zeigen.
Bei separaten Datenbanken ist die Isolation am stärksten, aber die Berechtigungslogik wandert in die Infrastruktur: die richtige DB-Verbindung wählen, Credentials verwalten und globale Mitarbeiterkonten handhaben.
Unabhängig vom Ansatz reduzieren einige Muster konsequent das Risiko:
- Lege
tenant_idin die Session oder in Claims des Auth-Tokens und behandle es als erforderlich. - Zentralisiere Mandantenprüfungen an einem Ort (Middleware oder eine gemeinsame Business-Process-Logik), nicht verstreut über Endpunkte.
- Zeige in Admin-Tools den Mandantenkontext deutlich an und erfordere einen expliziten Mandantenwechsel.
- Nutze für Supportzugriffe Impersonation mit Audit-Log.
In AppMaster bedeutet das typischerweise, den Mandantenkontext direkt nach der Authentifizierung zu speichern und ihn in API-Endpunkten und Business-Prozessen wiederzuverwenden, sodass jede Abfrage korrekt eingegrenzt bleibt. Ein Supportagent sollte Bestellungen nur sehen, nachdem die App den Mandantenkontext gesetzt hat, und nicht, weil die UI zufällig gefiltert ist.
Reporting und Performance beim tenant_id-Modell
Beim Single-Database-Ansatz mit tenant_id ist Reporting meist unkompliziert. Globale Dashboards (MRR, Signups, Nutzung) können eine einzelne Abfrage über alle Mandanten ausführen; Mandantenberichte sind dieselbe Abfrage mit Filter.
Der Kompromiss ist die Performance über Zeit. Wenn Tabellen wachsen, kann ein sehr aktiver Mandant zum „noisy neighbor“ werden, mehr Rows erzeugen, mehr Writes triggern und häufige Abfragen verlangsamen, falls die DB zu viele Zeilen scannen muss.
Indexierung hält dieses Modell gesund. Die meisten mandantenspezifischen Reads sollten einen Index nutzen, der mit tenant_id beginnt, damit die DB direkt zur relevanten Datenmenge springen kann.
Eine gute Basis:
- Füge Composite-Indexe hinzu, bei denen
tenant_iddie erste Spalte ist (z. B.tenant_id + created_at,tenant_id + status,tenant_id + user_id) - Behalte wirklich globale Indexe nur für nötige Cross-Tenant-Abfragen
- Achte auf Joins und Filter, die
tenant_id„vergessen“, denn das verursacht langsame Scans
Retention und Löschungen brauchen ebenfalls einen Plan, weil die Historie eines Mandanten Tabellen für alle aufblähen kann. Wenn Mandanten unterschiedliche Retention-Regeln haben, erwäge Soft-Deletes plus geplantes Archivieren pro Mandant oder das Verschieben alter Zeilen in eine Archiv-Tabelle, die nach tenant_id geordnet ist.
Ansatz 2: Separate Schemas pro Mandant
Bei separaten Schemas nutzt du weiterhin eine PostgreSQL-Datenbank, aber jeder Mandant bekommt sein eigenes Schema (z. B. tenant_42). Tabellen in diesem Schema gehören nur diesem Mandanten. Es fühlt sich an, als bekäme jeder Kunde eine „Mini-Datenbank“, ohne den Overhead vieler DB-Instanzen.
Eine übliche Konstellation hält globale Dienste in einem Shared-Schema und Mandantendaten in Mandantenschemas. Die Trennung richtet sich danach, was produktübergreifend geteilt werden muss und was niemals vermischt werden darf.
Typische Aufteilung:
- Shared-Schema: tenants-Tabelle, Pläne, Billing-Records, Feature-Flags, Audit-Einstellungen
- Tenant-Schema: Geschäftstabellen wie orders, tickets, inventory, projects, custom fields
- Je nach Produkt: users und roles, besonders wenn Benutzer mehrere Mandanten nutzen können
Dieses Modell reduziert das Risiko versehentlicher Cross-Tenant-Joins, weil Tabellen in unterschiedlichen Namespaces liegen. Es erleichtert auch Backups oder Restores für einen einzelnen Mandanten, da du ein Schema direkt anvisieren kannst.
Migrations sind oft die Überraschung. Wenn du eine neue Tabelle oder Spalte hinzufügst, musst du die Änderung auf jedes Mandantenschema anwenden. Bei 10 Mandanten ist das handhabbar. Bei 1.000 brauchst du Prozesse: Schema-Versionierung, Batch-Migrationen und sichere Fehlerbehandlung, damit ein kaputter Mandant nicht alle blockiert.
Gemeinsame Dienste wie Auth und Billing leben normalerweise außerhalb der Mandantenschemas. Ein praktikables Muster ist gemeinsame Auth (eine User-Tabelle mit einer Membership-Tabelle) und gemeinsames Billing (Stripe-Customer-IDs, Rechnungen), während Mandantenschemas die geschäftlichen Daten speichern.
Wenn du AppMaster nutzt, plane früh, wie Data-Designer-Modelle auf Shared- vs. Tenant-Schemas abgebildet werden, und halte globale Dienste stabil, damit Mandantenschemas sich weiterentwickeln können, ohne Login oder Zahlungen zu brechen.
Reporting und Performance mit separaten Schemas
Separate Schemas bieten standardmäßig stärkere Trennung als reines tenant_id-Filtern, weil Tabellen physisch getrennt sind und Berechtigungen auf Schema-Ebene gesetzt werden können.
Reporting ist hervorragend, wenn die meisten Berichte pro Mandant sind. Abfragen bleiben einfach, weil du nur aus den Tabellen eines Mandanten liest, ohne dauerhaft zu filtern. Dieses Modell erlaubt auch „besondere“ Mandanten, die extra Tabellen oder benutzerdefinierte Spalten brauchen, ohne alle anderen zu belasten.
Aggregiertes Reporting über alle Mandanten ist der Punkt, an dem Schemas anfangen zu stören. Entweder brauchst du eine Reporting-Schicht, die viele Schemas abfragt, oder du pflegst gemeinsame Summary-Tabellen in einem zentralen Schema.
Gängige Muster:
- Mandantenbezogene Dashboards, die nur das Schema dieses Mandanten abfragen
- Ein zentrales Analytics-Schema mit nächtlichen Rollups aus jedem Mandanten
- Exportjobs, die Mandantendaten in ein warehouse-freundliches Format kopieren
Performance ist meist gut für mandantenbezogene Workloads. Indexe sind pro Mandant kleiner, und starke Schreiblast in einem Schema betrifft andere weniger. Der Nachteil ist operativer Overhead: Einen neuen Mandanten anzulegen bedeutet Schema anlegen, Migrationen ausführen und sicherstellen, dass alle Schemas synchron bleiben, wenn sich das Modell ändert.
Schemas passen gut, wenn du strengere Isolation willst ohne viele Datenbanken oder wenn du erwartest, dass Mandanten Anpassungen brauchen.
Ansatz 3: Separate Datenbank pro Mandant
Bei einer separaten Datenbank pro Mandant bekommt jeder Kunde seine eigene DB (oder seine eigene DB auf demselben Server). Das ist die isolierteste Option: wenn die Daten eines Mandanten beschädigt oder sehr stark belastet werden, wirkt sich das deutlich weniger auf andere aus.
Sie eignet sich besonders für regulierte Umgebungen (Health, Finance, Government) oder Enterprise-Kunden, die harte Trennung, individuelle Retention-Regeln oder dedizierte Performance erwarten.
Onboarding wird zu einem Provisioning-Workflow. Wenn ein neuer Mandant anlegt wird, muss dein System eine DB erstellen oder klonen, das Basis-Schema (Tabellen, Indexe, Constraints) anwenden, Zugangsdaten sicher speichern und API-Requests auf die richtige DB routen.
Beim Bauen mit AppMaster ist die zentrale Design-Entscheidung, wo du das Mandantenverzeichnis (eine zentrale Zuordnung Mandant → DB-Verbindung) speicherst und wie du sicherstellst, dass jede Anfrage die richtige Verbindung nutzt.
Upgrades und Migrationen sind der größte Tradeoff. Eine Schema-Änderung ist nicht mehr „einmal ausführen“, sondern „für jeden Mandanten ausführen“. Das erhöht operativen Aufwand und Risiko, weshalb Teams oft Schema-Versionen führen und Migrationen als kontrollierte Jobs pro Mandant ausrollen.
Der Vorteil ist Kontrolle. Du kannst große Mandanten zuerst migrieren, Performance beobachten und Änderungen schrittweise ausrollen.
Reporting und Performance mit separaten Datenbanken
Separate Datenbanken sind am einfachsten zu durchdenken. Accidental Cross-Tenant-Reads sind deutlich seltener, und ein Berechtigungsfehler betrifft meist nur einen Mandanten.
Performance ist ebenfalls ein Vorteil. Schwere Abfragen, große Importe oder ein durchdrehender Report in Mandant A verlangsamen Mandant B nicht. Das schützt vor noisy neighbors und erlaubt Ressourcen-Tuning pro Mandant.
Der Nachteil zeigt sich im Reporting: Globales Analytics ist schwer, weil die Daten physisch verteilt sind. Bewährte Muster sind das Kopieren wichtiger Events oder Tabellen in eine zentrale Reporting-DB, Events an ein Warehouse senden, pro-Mandant-Berichte laufen lassen und Ergebnisse aggregieren (wenn Mandantenzahl klein ist) oder Produktmetriken getrennt von Kundendaten halten.
Operativer Aufwand ist ein weiterer Faktor: Mehr DBs bedeuten mehr Backups, Upgrades, Monitoring und Incident-Response. Du kannst auch schneller an Connection-Limits stoßen, weil jeder Mandant eigene Connection Pools braucht.
Häufige Fehler, die Datenlecks oder späteren Schmerz verursachen
Die meisten Multi-Tenant-Probleme sind keine großen Architekturfehler. Es sind kleine Auslassungen, die zu Sicherheitslücken, chaotischem Reporting und teuren Aufräumaktionen wachsen. Multi-Tenancy funktioniert, wenn Mandantentrennung zur Gewohnheit wird, nicht zu einem nachträglichen Feature.
Ein häufiger Leak entsteht, wenn in einer Tabelle das Mandanten-Feld fehlt, besonders in Join-Tabellen wie user_roles, invoice_items oder Tags. Alles sieht gut aus, bis ein Report oder eine Suche über diese Join-Tabelle verbindet und Zeilen eines anderen Mandanten zieht.
Ein weiteres häufiges Problem sind Admin-Dashboards, die Mandantenfilter umgehen. Das beginnt oft mit „nur für Support“ und wird dann wiederverwendet. No-Code-Tools ändern nichts daran: jede Abfrage, jeder Business-Process und jeder Endpoint, der Mandantendaten liest, braucht denselben Mandantenkontext.
IDs können auch Probleme machen. Wenn du menschenfreundliche IDs zwischen Mandanten teilst (z. B. order_number = 1001) und annimmst, sie seien global eindeutig, mischen Support-Tools und Integrationen Datensätze. Halte mandantenbezogene Identifikatoren getrennt von internen Primärschlüsseln und füge Mandantenkontext in Lookups ein.
Schließlich unterschätzen Teams Migrationen und Backups beim Skalieren. Was bei 10 Mandanten einfach ist, kann bei 1.000 langsam und riskant werden.
Schnelle Prüfungen, die viel Schmerz verhindern:
- Mache Mandantenbesitz auf jeder Tabelle explizit, einschließlich Join-Tabellen.
- Nutze ein einheitliches Mandanten-Scoping-Muster und verwende es überall wieder.
- Sorge dafür, dass Reports und Exporte nicht ohne Mandantenkontext laufen können (es sei denn, sie sind wirklich global).
- Vermeide mandanten-uneindeutige Identifikatoren in APIs und Support-Tools.
- Übe Restore- und Migrationsschritte früh, nicht erst nach Wachstum.
Beispiel: Ein Supportagent sucht nach „Rechnung 1001“ und zieht den falschen Mandanten, weil das Lookup den Mandantenkontext übersprang. Ein kleiner Bug mit großer Auswirkung.
Schnell-Checkliste bevor du dich festlegst
Bevor du ein Multi-Tenant-Datenmodell finalisierst, führe ein paar Tests durch. Ziel ist, Datenlecks früh zu entdecken und sicherzustellen, dass deine Wahl auch bei großen Tabellen funktioniert.
Schnellchecks, die du in einem Tag machen kannst
- Datenisolation prüfen: Erstelle zwei Mandanten (A und B), lege ähnliche Datensätze an und verifiziere, dass jedes Lesen und Aktualisieren auf den aktiven Mandanten eingegrenzt ist. Verlass dich nicht nur auf UI-Filter.
- Permission-Break-Test: Logge dich als Nutzer von Mandant A ein und versuche, einen Datensatz von Mandant B zu öffnen, zu bearbeiten oder zu löschen, indem du nur die Datensatz-ID änderst. Wenn etwas gelingt, ist das ein Blocker für die Veröffentlichung.
- Schreibpfad-Sicherheit: Bestätige, dass neue Datensätze immer den korrekten Mandantenwert erhalten (oder im richtigen Schema/DB landen), auch wenn sie von Hintergrundjobs, Importen oder Automatisierungen erstellt werden.
- Reporting-Probelauf: Prüfe, dass du mandantenspezifisches Reporting und „alle Mandanten“-Reporting (für internes Personal) durchführen kannst, mit klaren Regeln, wer die globale Ansicht sehen darf.
- Performance-Check: Implementiere jetzt eine Index-Strategie (insbesondere für
(tenant_id, created_at)und andere gängige Filter) und messe absichtlich mindestens eine langsame Abfrage, damit du weißt, wie „schlecht“ aussieht.
Um den Reporting-Test konkret zu machen, wähle zwei Fragen, die du brauchst (eine mandantenspezifisch, eine global), und führe sie gegen Beispieldaten aus.
-- Tenant-only: last 30 days, one tenant
SELECT count(*)
FROM tickets
WHERE tenant_id = :tenant_id
AND created_at >= now() - interval '30 days';
-- Global (admin): compare tenants
SELECT tenant_id, count(*)
FROM tickets
WHERE created_at >= now() - interval '30 days'
GROUP BY tenant_id;
Wenn du in AppMaster prototypierst, baue diese Prüfungen in deine Business-Process-Flows (read, write, delete) ein und lege zwei Mandanten im Data Designer an. Wenn diese Tests mit realistischem Datenvolumen bestehen, kannst du mit Vertrauen zusagen.
Beispielszenario: Von den ersten Kunden bis zum Skalieren
Ein 20-köpfiges Unternehmen startet ein Kundenportal für Rechnungen, Tickets und ein einfaches Dashboard. Erwartet werden 10 Mandanten im ersten Monat und ein Wachstum auf 1.000 innerhalb eines Jahres.
Früh ist das einfachste Modell meist eine einzige Datenbank, in der jede Kundendaten-Tabelle tenant_id enthält. Es ist schnell aufzubauen, einfach zu reporten und vermeidet duplizierte Setups.
Bei 10 Mandanten ist das größte Risiko nicht Performance, sondern Berechtigungen. Ein vergessener Filter (z. B. eine „Liste Rechnungen“-Abfrage ohne tenant_id) kann Daten leaken. Das Team sollte Mandantenprüfungen an einer konsistenten Stelle erzwingen (gemeinsame Business-Logik oder wiederverwendbare API-Muster) und Mandanten-Scoping als nicht verhandelbar ansehen.
Beim Wachstum von 10 auf 1.000 Mandanten ändern sich die Bedürfnisse. Reporting wird schwerer, Support fordert „Exportiere alles für diesen Mandanten“ und einige große Mandanten dominieren Traffic und verlangsamen geteilte Tabellen.
Ein praktischer Upgrade-Pfad sieht oft so aus:
- Behalte Anwendungslogik und Berechtigungsregeln, verschiebe aber stark genutzte Mandanten in separate Schemas.
- Für die größten Mandanten (oder Compliance-Kunden) verschiebe sie in separate Datenbanken.
- Behalte eine gemeinsame Reporting-Schicht, die aus allen Mandanten liest, und plane schwere Reports außerhalb der Spitzenzeiten.
Wähle das einfachste Modell, das heute Daten sicher trennt, und plane einen Migrationspfad für „wenige riesige Mandanten“, statt dafür am ersten Tag zu optimieren.
Nächste Schritte: Wähle ein Modell und prototypisiere es in einem No-Code-Backend
Entscheide dich danach, was du zuerst schützen musst: Datenisolation, operative Einfachheit oder Mandanten-spezifisches Skalieren. Sicherheit gewinnst du, indem du einen kleinen Prototyp baust und versuchst, ihn mit realen Berechtigungs- und Reporting-Szenarien zu brechen.
Eine einfache Startanleitung:
- Wenn die meisten Mandanten klein sind und du einfaches Cross-Tenant-Reporting brauchst, starte mit einer Datenbank und
tenant_idin jeder Zeile. - Wenn du stärkere Trennung willst, aber eine einzige DB verwalten möchtest, ziehe separate Schemas pro Mandant in Betracht.
- Wenn Mandanten harte Isolation verlangen (Compliance, dedizierte Backups, noisy-neighbor-Risiko), denke an separate Datenbanken pro Mandant.
Schreibe vor dem Bau Mandantengrenzen in Klartext. Definiere Rollen (Owner, Admin, Agent, Viewer), was jede Rolle darf und was „global“ bedeutet (Pläne, Templates, Audit-Logs). Entscheide, wie Reporting funktionieren soll: nur mandantenspezifisch oder „alle Mandanten“ für internes Personal.
Wenn du AppMaster nutzt, kannst du diese Muster schnell prototypen: Modellier Tabellen im Data Designer (einschließlich tenant_id, Unique-Constraints und Indexe, auf die deine Abfragen angewiesen sind) und setze Regeln im Business-Process-Editor, damit jedes Lesen und Schreiben mandantengebunden bleibt. Wenn du einen Anhaltspunkt für die Plattform brauchst, ist AppMaster unter appmaster.io verfügbar.
Ein praktischer finaler Test: Erstelle zwei Mandanten (A und B), lege ähnliche Nutzer und Bestellungen an und führe dieselben Abläufe für beide aus. Versuche, einen Report für Mandant A zu exportieren und übergib absichtlich IDs von Mandant B an dieselben Endpunkte. Dein Prototyp ist „sicher genug“, wenn diese Versuche jedes Mal fehlschlagen und deine wichtigen Reports mit realistischem Datenvolumen weiterhin schnell sind.
FAQ
Default: Verwende eine einzelne Datenbank mit tenant_id auf jeder tenant-eigenen Tabelle, wenn du die einfachste Betriebsführung und häufige Analyse über alle Mandanten brauchst. Wechsle zu separaten Schemas, wenn du stärkere Isolation oder individuelle Anpassungen pro Mandant willst, ohne viele Datenbanken zu betreiben. Wähle separate Datenbanken, wenn Compliance- oder Enterprise-Anforderungen harte Trennung und individuelle Performance-Kontrolle erfordern.
Behandle Mandanten-Scoping als Pflicht im Backend, nicht als UI-Filter. Mache tenant_id auf tenant-eigenen Tabellen verpflichtend und leite es immer aus dem authentifizierten Benutzerkontext ab, statt dem Client zu vertrauen. Ergänze bei Bedarf eine Sicherheitsnet wie PostgreSQL Row-Level Security und baue Tests, die versuchen, durch Änderung nur einer ID auf einen anderen Mandanten zuzugreifen.
Setze tenant_id an den Anfang der Indexe, die deinen häufigen Filtern entsprechen, damit die Datenbank direkt in die Teilmenge eines Mandanten springen kann. Eine übliche Basis ist (tenant_id, created_at) für zeitbasierte Ansichten und zusätzliche (tenant_id, status) oder (tenant_id, user_id) für Dashboard-Filter. Sorge außerdem dafür, dass Eindeutigkeitsregeln mandantenspezifisch sind (z. B. E-Mail eindeutig pro Mandant), um Kollisionen zu vermeiden.
Separate Schemas reduzieren versehentliche Cross-Tenant-Joins, weil Tabellen in unterschiedlichen Namensräumen liegen, und erlauben Berechtigungen auf Schema-Ebene. Der Hauptnachteil sind Migrationen: jede Änderung muss in jedem Schema angewendet werden, was bei wachsender Mandantenanzahl zu einem Prozessproblem wird. Es ist ein guter Mittelweg, wenn du stärkere Isolation als mit tenant_id willst, aber nur eine Datenbank verwalten möchtest.
Separate Datenbanken minimieren die Blast-Radius: Performance-Spikes, Fehlkonfigurationen oder Korruption bleiben höchstwahrscheinlich bei einem Mandanten. Der Preis ist erhöhter operativer Aufwand, denn Provisioning, Backups, Monitoring und Migrationen vervielfachen sich. Du brauchst außerdem ein zuverlässiges Mandantenverzeichnis und Request-Routing, damit jeder API-Aufruf die richtige DB-Verbindung nutzt.
Cross-Tenant-Reporting ist mit einer einzelnen DB und tenant_id am einfachsten, da globale Dashboards einfach Abfragen ohne Mandantenfilter sind. Bei Schemas oder separaten DBs funktioniert globales Analytics meist, indem man Schlüsselereignisse oder Zusammenfassungen in einen gemeinsamen Reporting-Store kopiert (zeitgesteuert). Halte die Regel einfach: Produktweite Metriken landen im Reporting-Layer, Mandantendaten bleiben isoliert.
Mache den Mandantenkontext in Support-Tools explizit und erfordere einen bewussten Mandantenwechsel, bevor Datensätze angezeigt werden. Wenn du Impersonation nutzt, protokolliere genau, wer wann was angesehen hat, und mache die Erlaubnis zeitlich begrenzt. Vermeide Support-Workflows, die nur eine Datensatz-ID ohne Mandantenkontext akzeptieren — so entstehen Fehler wie „Rechnung 1001“ aus einem anderen Mandanten.
Wenn Mandanten unterschiedliche Felder oder Abläufe brauchen, reduzieren Schemas oder separate Datenbanken das Risiko, dass Änderungen eines Mandanten andere beeinflussen. Wenn die meisten Mandanten ähnlich sind, bleibe bei einem gemeinsamen Modell mit tenant_id und löse Unterschiede über konfigurierbare Optionen wie Feature Flags oder optionale Felder. Vermeide Tabellen mit uneindeutiger Besitzverteilung zwischen global und mandantenspezifisch.
Bestimme früh die Mandantengrenze: entscheide, wo der Mandantenkontext nach der Authentifizierung gespeichert wird und sorge dafür, dass jede Lese- und Schreiboperation ihn nutzt. In AppMaster bedeutet das typischerweise, tenant_id aus dem authentifizierten Benutzer in deiner Business-Process-Logik zu setzen, bevor tenant-eigene Datensätze erstellt oder abgefragt werden. Betrachte das als wiederverwendbares Muster, das überall angewendet wird.
Lege zwei Mandanten mit ähnlichen Daten an und versuche, die Isolation zu brechen, indem du nur Datensatz-IDs bei Lese-, Update- und Delete-Operationen änderst. Überprüfe, dass Hintergrundjobs, Importe und Exporte weiterhin in den richtigen Mandantenkontext schreiben, denn diese Pfade werden leicht übersehen. Führe außerdem je einen Mandantenbericht und einen globalen Adminbericht gegen realistische Testdaten aus, um Performance und Zugriffsregeln zu prüfen.


