Eine geschäftliche Anfrage wird gefährlich, wenn sie keine explizite Produktentscheidung mehr ist und sich als if ($tenantId === ...) materialisiert. Zunächst löst sie ein dringendes Problem. Mit der Zeit taucht diese Bedingung in Controllern, Templates, Queue-Prozessen, Exporten und APIs auf. Das Ergebnis ist keine Konfiguration: Es sind implizite Produktvarianten, die schwer zu testen, zu erklären und zu entfernen sind.
Die Mandantenkonfiguration in einem PHP-SaaS muss bewusste und gesteuerte Unterschiede ermöglichen, nicht jede historische Ausnahme bewahren. Die hilfreiche Frage lautet nicht „Können wir das für diesen Kunden umsetzen?“, sondern „Stellt diese Variation eine stabile Produktdimension dar, die andere Kunden mit nachhaltigen Regeln und Support benötigen könnten?“.
Das Warnsignal: eine dauerhafte Code-Ausnahme

Es gibt einen Unterschied zwischen der Anpassung eines Nutzungserlebnisses und der Pflege eines verborgenen Produktzweigs. Es empfiehlt sich, einzugreifen, bevor eine spezifische Anfrage eines dieser Signale erzeugt:
- Die Mandanten-, Domain- oder Kundenkennung erscheint in der Geschäftslogik.
- Dieselbe Regel wird in Benutzeroberfläche, API und asynchronem Worker dupliziert.
- Das Team kann nicht beantworten, welche Kunden eine Ausnahme haben oder wer sie genehmigt hat.
- Eine Planänderung verändert funktionale Verhaltensweisen ohne zentrale Definition.
- Das Entfernen einer Anpassung erfordert, Bedingungen in mehreren Repositories oder Services zu suchen.
Eine Ausnahme kann während einer Discovery-Phase oder Migration legitim sein, muss jedoch einen Verantwortlichen, ein Überprüfungsdatum und einen Ausweg haben: sie in eine Produktfunktion überführen, sie als spezifische Integration isolieren oder sie ablehnen. Sie ohne Klassifizierung zu belassen, macht technische Schulden zu einem nicht dokumentierten geschäftlichen Versprechen.
Verwechseln Sie Konfiguration, Berechtigungen, Funktionsfreigaben und kundenspezifische Entwicklung nicht
Diese Mechanismen beantworten unterschiedliche Fragen. Sie zu vermischen führt zu intransparenten Designs und widersprüchlichen Regeln.
- Konfiguration: Definiert, wie sich eine bereits vorhandene Funktion für einen Mandanten verhält. Beispielsweise das Format einer Nummerierung, die Standardsprache oder ob ein Ablauf eine zusätzliche Genehmigung erfordert.
- Berechtigungen: Bestimmen, was eine Identität innerhalb eines Mandanten tun darf. Ein Benutzer kann berechtigt sein, Zahlungen zu genehmigen, obwohl die Genehmigung als verpflichtend konfiguriert ist.
- Funktionsfreigaben: Zeigen an, ob der Mandant Zugriff auf eine Funktion oder ein operatives Limit hat. Sie können von Vertrag, Plan oder kontrollierter Aktivierung abhängen, sollten jedoch nicht die gesamte Domänenlogik enthalten.
- Kundenspezifische Entwicklung: Deckt ein Verhalten ab, das nicht in eine wiederverwendbare Produktdimension passt, etwa eine Integration mit einem kundeneigenen System oder eine spezifische Transformation aufgrund vertraglicher Vorgaben.
Eine praktische Regel hilft bei der Entscheidung: Wenn sich ändert, wer eine Aktion ausführt, verwenden Sie Berechtigungen; wenn sich ändert, ob eine Funktion existiert oder verfügbar ist, verwenden Sie Funktionsfreigaben; wenn sich ändert, wie eine verfügbare Funktion arbeitet, verwenden Sie Konfiguration. Wenn sich das Geschäftsmodell exklusiv ändert, tarnen Sie es nicht als Flag.
Was konfigurierbar sein sollte und was im Kern bleiben muss
Eine Option sollte in den Konfigurationskatalog aufgenommen werden, wenn sie eine klare Semantik, eine endliche Wertemenge, bekannte Validierungen und eine angemessene Erwartung an Wiederverwendbarkeit hat. Sie benötigt außerdem eine verständliche Support-Erfahrung: Jemand muss die Auswirkung einer Änderung erklären können, ohne Code zu untersuchen.
Gute Kandidaten sind häufig Darstellungsparameter, Benachrichtigungsrichtlinien, Schwellenwerte, Genehmigungssequenzen, regionale Präferenzen und Auswahlen zwischen bereits unterstützten Abläufen. Dagegen müssen Sicherheitsinvarianten, Datenintegrität, grundlegende Finanzberechnungen und Regeln, deren Änderung eine Neuinterpretation bestehender Entitäten oder Verträge erfordern würde, im Kern bleiben.
Verwandeln Sie nicht beliebige Daten allein wegen der Flexibilität in Konfiguration. Ein JSON-Feld ohne Schema kann Abhängigkeiten verbergen, die unmöglich zu entdecken sind. Wenn eine Option eine kritische Regel verändert, definieren Sie Typen, zulässige Werte, Bedingungen für ihre Verwendung und Folgen für frühere Daten.
Erstellen Sie ein gesteuertes Konfigurationsmodell
Ein isolierter Schlüssel reicht nicht aus. Jede Katalogdefinition muss Metadaten enthalten, mit denen das Produkt sicher betrieben werden kann:
- Schlüssel und funktionale Beschreibung: stabile Namen, die sich an der Domäne und nicht an Implementierungsdetails orientieren.
- Verantwortlicher: Team oder zuständige Person, die über Weiterentwicklung und Entfernung entscheidet.
- Geltungsbereich: global, Mandant, Organisationseinheit, Projekt oder Benutzer. Vermeiden Sie, standardmäßig alle Geltungsbereiche zu erlauben.
- Standardwert: explizites Verhalten, wenn keine Überschreibung existiert.
- Typ und Validierung: Boolean, Enumeration, Zahl mit Wertebereich oder mittels Schema validierte Struktur.
- Abhängigkeiten: Anforderungen in Bezug auf andere Optionen, Funktionsfreigaben oder Migrationsstatus.
- Sensibilität: Datenklassifizierung und Zugriffsregeln für Lesen und Ändern.
- Lebenszyklus: Einführungsdatum, Überprüfungsdatum, Datum der Ausphasung und vorgesehenes Entfernungsdatum, sofern zutreffend.
Zentralisieren Sie in PHP die Auflösung in einem Domänenservice, beispielsweise TenantSettings, und liefern Sie typisierte Objekte statt Arrays ohne Vertrag. Die Anwendung kann den globalen Wert, den Mandantenwert und einen Wert mit einem spezifischeren Geltungsbereich über eine dokumentierte Priorität kombinieren. Ist kein Wert vorhanden, muss immer der Standardwert gelten, nicht je Consumer eine andere Interpretation.
$policy = $tenantSettings->approvalPolicy($tenantId);
if ($policy->requiresSecondApproval()) {
$workflow->requestSecondApproval($order);
}
Die Speicherung kann relational oder dokumentenorientiert sein, aber Katalog und Validierung sollten nicht von der Persistenzform abhängen. Führen Sie außerdem eine unveränderliche Änderungshistorie: vorheriger und neuer Wert, Akteur, Zeitpunkt, Grund und Änderungskanal. Die Historie ersetzt kein Auditprotokoll von Geschäftsaktionen, ermöglicht jedoch die Rekonstruktion, welche Konfiguration gültig war.
Treffen Sie die Entscheidung an der richtigen architektonischen Grenze
Das Problem verstreuter Bedingungen wird nicht gelöst, indem man sie alle in einen Controller verschiebt. Eine Konfiguration, die eine Geschäftsregel betrifft, muss in dem Domänenservice oder der Domänenrichtlinie ausgewertet werden, die diese Regel anwendet. Der Controller übersetzt die Anfrage; das Template stellt das Ergebnis dar; keines von beiden sollte eigenständig über eine Mandantenrichtlinie entscheiden.
Verwenden Sie bei komplexen Verhaltensweisen registrierte Strategien oder Richtlinien statt Ketten von Booleans. Eine Abrechnungsrichtlinie kann eine Implementierung aus unterstützten Modi auswählen, nachdem validiert wurde, dass der Mandant über die erforderliche Funktionsfreigabe verfügt. So rufen Benutzeroberfläche, API und Queue dieselbe Entscheidung auf.
Templates können eine bereits vorbereitete Ansicht erhalten, einschließlich Indikatoren für Funktionsfreigaben zum Anzeigen oder Ausblenden von Aktionen. Das Ausblenden einer Schaltfläche ist keine Autorisierung. Die API muss Berechtigungen, Funktionsfreigaben und Konfiguration auf dem Server anwenden, auch wenn die Benutzeroberfläche die Operation nicht anbietet.
Funktionsfreigaben und Limits ohne Versteifung der Pläne
Ein kommerzieller Plan kann Funktionsfreigaben gewähren, darf jedoch nicht zu einer Sammlung von if ($plan === '...') werden. Modellieren Sie eine stabile Funktionsfreigabe wie advanced_approvals oder api_access und ermitteln Sie anhand einer vertraglichen oder administrativen Quelle, welche Mandanten sie besitzen. Anschließend fragt die funktionale Logik die Funktionsfreigabe ab, nicht den Namen des Plans.
Limits erfordern eine noch präzisere Definition: Was wird gezählt, in welchem Zeitfenster, wann wird die Sperre angewendet und wie verhalten sich Wiederholungsversuche und Queue-Prozesse? Ein Limit muss an allen Einstiegspunkten beobachtbar und konsistent sein. Wenn eine Integration Ressourcen außerhalb der Hauptbenutzeroberfläche erstellt, darf sie dieselbe Kontrolle nicht umgehen.
Ändern Sie Einstellungen sicher und reversibel
Das Ändern einer Option kann unmittelbare Auswirkungen auf laufende Aufgaben, bestehende Datensätze oder Integrationen haben. Validieren Sie vor dem Speichern Typ, administrative Berechtigungen, Abhängigkeiten und Kompatibilität mit dem aktuellen Zustand. Wenn die Auswirkungen relevant sind, bieten Sie eine Vorschau der Änderung an: welcher Ablauf aktiviert wird, welche Einschränkungen sie verletzt und welche künftigen Operationen sie betreffen wird.
Eine schrittweise Aktivierung unterscheidet sich davon, eine Option in der gesamten Benutzeroberfläche verfügbar zu machen. Sie können eine Funktionsfreigabe für eine kontrollierte Gruppe von Mandanten aktivieren und das Verhalten dieser Mandanten beobachten, bevor Sie sie allgemein verfügbar machen. Definieren Sie auch eine Rückabwicklung: Welcher Wert den vorherigen Zustand wiederherstellt, ob damit Datenmigrationen verbunden sind und was mit Operationen geschieht, die unter der neuen Konfiguration gestartet wurden.
Eine in der Benutzeroberfläche reversible Änderung ist möglicherweise in den Daten nicht reversibel. Behandeln Sie beide Dimensionen getrennt, bevor Sie eine neue Richtlinie aktivieren.
Konsistenz in Queues, APIs und Integrationen wahren
Asynchrone Prozesse führen eine zusätzliche Entscheidung ein: die Konfiguration bei Ausführung der Aufgabe aufzulösen oder beim Erstellen eine Momentaufnahme zu bewahren. Für Aktionen, die die gültige Richtlinie beachten müssen, lösen Sie sie bei der Ausführung auf und schließen den Mandanten in den Aufgabenkontext ein. Für Dokumente, Berechnungen oder Kommunikationen, die die ursprüngliche Entscheidung reproduzieren müssen, speichern Sie eine explizite Version oder Momentaufnahme zusammen mit dem Command.
Vermischen Sie beide Optionen nicht, ohne dies zu deklarieren. Ein Wiederholungsversuch kann sein Ergebnis ändern, wenn er eine aktualisierte Konfiguration abfragt. Definieren Sie Idempotenz, Konfigurationsversion und erwartetes Verhalten bei Wiederholungsversuchen. Externe Integrationen benötigen gleichwertige Verträge: vorherige Validierung, Fehlerbehandlung, Limits und Nachverfolgbarkeit pro Mandant, ohne Geheimnisse oder personenbezogene Daten an das Diagnoseprotokoll zu senden.
Audit und Support: das beobachtete Verhalten erklären

Der Support muss beantworten können, warum ein Kunde einen Ablauf sieht, und nicht nur, welchen Wert ein Schlüssel hat. Protokollieren Sie eine Entscheidungsnachverfolgung mit Mandantenkennung, Definitionsversion, Quelle des effektiven Werts — Standardwert oder Überschreibung —, relevanten Funktionsfreigaben und Ergebnis der Auswertung. Beschränken Sie den Zugriff auf diese Informationen und maskieren Sie sensible Werte.
Ergänzen Sie diese Nachverfolgbarkeit durch Nutzungsmetriken pro Option, Validierungsfehler, fehlgeschlagene Änderungen und Optionen ohne Nutzung. Eine ungenutzte Konfiguration kann veraltet sein; eine, die lange Zeit von nur einem Mandanten verwendet wird, verdient eine Produktüberprüfung. Das Ziel ist nicht, jeden Unterschied zu beseitigen, sondern jeden Unterschied explizit, überprüfbar und beobachtbar zu machen und ihn zu entfernen, wenn er keinen Mehrwert mehr liefert.



