Ga direct naar de inhoud
DedicatedPHP Contact

Klantconfiguratie in een PHP SaaS zonder onbeheersbare varianten

Ontwerp beheerde klantconfiguratie in een PHP SaaS om aan B2B-behoeften te voldoen zonder vertakkingen of conditionals te vermenigvuldigen.

Redactioneel diagram van een PHP SaaS met gecentraliseerde klantconfiguratie, rechten, capaciteiten en asynchrone processen

Een commerciële aanvraag wordt gevaarlijk wanneer die geen expliciete productbeslissing meer is en gestalte krijgt als een if ($tenantId === ...). In het begin lost dat een urgente behoefte op. Na verloop van tijd duikt die voorwaarde op in controllers, templates, queueprocessen, exports en API's. Het resultaat is geen configuratie: het zijn impliciete productvarianten die moeilijk te testen, uit te leggen en te verwijderen zijn.

Klantconfiguratie in een PHP SaaS moet weloverwogen en beheerde verschillen mogelijk maken, en niet elke historische uitzondering in stand houden. De nuttige vraag is niet: «kunnen we dit voor deze klant doen?», maar: «vertegenwoordigt deze variatie een stabiele productdimensie die andere klanten mogelijk nodig hebben, met duurzame regels en ondersteuning?».

Het alarmsignaal: een permanente code-uitzondering

Het alarmsignaal: een permanente code-uitzondering — guía visual de DedicatedPHP

Er is een verschil tussen het aanpassen van een ervaring en het onderhouden van een verborgen producttak. Het is raadzaam in te grijpen voordat een specifiek verzoek een van deze signalen creëert:

  • De tenant-, domein- of klantidentifier verschijnt in bedrijfslogica.
  • Dezelfde regel wordt gerepliceerd in de interface, API en asynchrone worker.
  • Het team kan niet beantwoorden welke klanten een uitzondering hebben of wie die heeft goedgekeurd.
  • Een wijziging van abonnement verandert functioneel gedrag zonder een centrale definitie.
  • Het verwijderen van een aanpassing vereist het zoeken naar conditionals in meerdere repositories of services.

Een uitzondering kan legitiem zijn tijdens een verkenning of migratie, maar moet een eigenaar, beoordelingsdatum en uitweg hebben: er een productcapaciteit van maken, deze als specifieke integratie isoleren of afwijzen. Een uitzondering die ongeclassificeerd blijft, verandert technische schuld in een niet-gedocumenteerde commerciële belofte.

Verwar configuratie, rechten, capaciteiten en maatwerkontwikkeling niet

Deze mechanismen beantwoorden verschillende vragen. Ze door elkaar halen leidt tot ondoorzichtige ontwerpen en tegenstrijdige regels.

  • Configuratie: bepaalt hoe een bestaande functie zich gedraagt voor een tenant. Bijvoorbeeld de indeling van een nummering, de standaardtaal of of een flow een extra goedkeuring vereist.
  • Rechten: bepalen wat een identiteit binnen een tenant mag doen. Een gebruiker kan toestemming hebben om betalingen goed te keuren, ook wanneer goedkeuring als verplicht is geconfigureerd.
  • Capaciteiten: geven aan of de tenant toegang heeft tot een functie of operationele limiet. Ze kunnen afhangen van contract, abonnement of gecontroleerde activering, maar zouden niet alle domeinlogica moeten bevatten.
  • Maatwerkontwikkeling: dekt gedrag dat niet past in een herbruikbare productdimensie, zoals een integratie met een eigen systeem van de klant of een unieke contractuele transformatie.

Een praktische regel helpt bij de keuze: verandert wie een actie uitvoert, gebruik dan rechten; verandert of een functie bestaat of beschikbaar is, gebruik dan capaciteiten; verandert hoe een beschikbare functie werkt, gebruik dan configuratie. Als het bedrijfsmodel verandert op een manier die uitsluitend voor één klant geldt, vermom het dan niet als functionaliteitsschakelaar.

Wat configureerbaar moet zijn en wat in de kern moet blijven

Een optie verdient een plaats in de configuratiecatalogus wanneer zij duidelijke semantiek, een eindige set waarden, bekende validaties en een redelijke verwachting van hergebruik heeft. Ze heeft ook een begrijpelijke supportervaring nodig: iemand moet het effect van een wijziging kunnen uitleggen zonder code te inspecteren.

Presentatieparameters, notificatiebeleid, drempelwaarden, goedkeuringsreeksen, regionale voorkeuren en keuzes tussen al ondersteunde flows zijn doorgaans goede kandidaten. Daarentegen moeten beveiligingsinvarianten, data-integriteit, financiële basisberekeningen en regels waarvan een wijziging zou vereisen dat bestaande entiteiten of contracten opnieuw worden geïnterpreteerd, in de kern blijven.

Maak niet willekeurige data tot configuratie enkel omwille van flexibiliteit. Een JSON-veld zonder schema kan afhankelijkheden verbergen die onmogelijk te ontdekken zijn. Wanneer een optie een kritieke regel wijzigt, definieer dan typen, toegestane waarden, gebruiksvoorwaarden en gevolgen voor eerdere data.

Bouw een beheerd configuratiemodel

Een losse sleutel is niet genoeg. Elke catalogusdefinitie moet metadata bevatten die het mogelijk maakt het product veilig te beheren:

  • Sleutel en functionele beschrijving: stabiele namen, gericht op het domein en niet op implementatiedetails.
  • Eigenaar: team of verantwoordelijke die beslist over de evolutie en verwijdering ervan.
  • Scope: globaal, tenant, organisatorische eenheid, project of gebruiker. Vermijd standaard alle scopes toe te staan.
  • Standaardwaarde: expliciet gedrag wanneer er geen afwijkende waarde bestaat.
  • Type en validatie: boolean, enumeratie, getal met bereik of een via schema gevalideerde structuur.
  • Afhankelijkheden: vereisten ten opzichte van andere opties, capaciteiten of migratiestatus.
  • Gevoeligheid: dataclassificatie en toegangsregels voor lezen en wijzigen.
  • Levenscyclus: datum van introductie, beoordeling, deprecatie en geplande verwijdering waar van toepassing.

Centraliseer in PHP de resolutie in een domeinservice, bijvoorbeeld TenantSettings, en lever getypeerde objecten in plaats van arrays zonder contract. De applicatie kan de globale waarde, tenantwaarde en een specifiekere waarde combineren volgens een gedocumenteerde prioriteit. De afwezigheid van een waarde moet altijd worden opgelost naar de standaardwaarde, niet naar een andere interpretatie bij elke afnemer.

$policy = $tenantSettings->approvalPolicy($tenantId);
if ($policy->requiresSecondApproval()) {
    $workflow->requestSecondApproval($order);
}

De opslag kan relationeel of documentgebaseerd zijn, maar de catalogus en validatie zouden niet afhankelijk mogen zijn van de vorm van persistentie. Bewaar daarnaast een onveranderlijke wijzigingshistorie: oude en nieuwe waarde, actor, tijdstip, reden en wijzigingskanaal. De historie vervangt geen auditlog van bedrijfsacties, maar maakt het mogelijk te reconstrueren welke configuratie van kracht was.

Evalueer de beslissing op de juiste grens

Het probleem van verspreide conditionals wordt niet opgelost door ze allemaal naar een controller te verplaatsen. Configuratie die een bedrijfsregel beïnvloedt, moet worden geëvalueerd in de domeinservice of het domeinbeleid dat die regel toepast. De controller vertaalt het verzoek; het template presenteert het resultaat; geen van beide zou zelf een tenantbeleid moeten bepalen.

Gebruik voor complex gedrag geregistreerde strategieën of beleidsregels, in plaats van ketens van booleans. Een factureringsbeleid kan een implementatie selecteren uit ondersteunde modi nadat is gevalideerd dat de tenant de benodigde capaciteit heeft. Zo roepen de interface, API en queue dezelfde beslissing aan.

Templates kunnen een reeds voorbereide view ontvangen, inclusief capaciteitsindicatoren om acties te tonen of te verbergen. Een knop verbergen is geen autorisatie. De API moet rechten, capaciteit en configuratie op de server toepassen, ook wanneer de interface de bewerking niet aanbiedt.

Capaciteiten en limieten zonder abonnementen star te maken

Een commercieel abonnement kan capaciteiten toekennen, maar mag geen verzameling if ($plan === '...') worden. Modelleer een stabiele capaciteit, zoals advanced_approvals of api_access, en bepaal via een contractuele of administratieve bron welke tenants die bezitten. Daarna raadpleegt de functionele logica de capaciteit, niet de abonnementsnaam.

Limieten vereisen een nog nauwkeurigere definitie: wat wordt geteld, binnen welk tijdvenster, wanneer de blokkering wordt toegepast en hoe herhaalpogingen en queueprocessen zich gedragen. Een limiet moet observeerbaar en consistent zijn op alle toegangspunten. Als een integratie resources creëert buiten de hoofdinterface, mag die dezelfde controle niet omzeilen.

Wijzig instellingen veilig en omkeerbaar

Het wijzigen van een optie kan onmiddellijke gevolgen hebben voor lopende taken, bestaande records of integraties. Valideer vóór het opslaan het type, administratieve rechten, afhankelijkheden en compatibiliteit met de huidige status. Wanneer de impact relevant is, bied dan een voorafgaande weergave van de wijziging: welke flow wordt geactiveerd, welke restricties zij schendt en welke toekomstige bewerkingen zij zal beïnvloeden.

Geleidelijke activering verschilt van het beschikbaar stellen van een optie in de volledige interface. U kunt een capaciteit voor een gecontroleerde set tenants inschakelen en het gedrag ervan observeren voordat u die algemeen beschikbaar maakt. Definieer ook een terugdraaiing: welke waarde de vorige status herstelt, of er gekoppelde datamigraties zijn en wat er gebeurt met bewerkingen die onder de nieuwe configuratie zijn gestart.

Een wijziging die in de interface omkeerbaar is, kan dat in de data niet zijn. Behandel beide dimensies afzonderlijk voordat u een nieuw beleid activeert.

Behoud consistentie in queues, API's en integraties

Asynchrone processen voegen een extra beslissing toe: de configuratie oplossen bij het uitvoeren van de taak of een momentopname bewaren bij het aanmaken ervan. Voor acties die het geldende beleid moeten respecteren, lost u die op bij uitvoering en neemt u de tenant op in de taakcontext. Voor documenten, berekeningen of communicatie die de oorspronkelijke beslissing moeten reproduceren, bewaart u een expliciete versie of momentopname naast het commando.

Combineer beide opties niet zonder dit te verklaren. Een herhaalpoging kan een ander resultaat opleveren als die een bijgewerkte configuratie raadpleegt. Definieer idempotentie, configuratieversie en verwacht gedrag bij herhaalpogingen. Externe integraties hebben equivalente contracten nodig: voorafgaande validatie, foutafhandeling, limieten en traceerbaarheid per tenant, zonder geheimen of persoonsgegevens naar het diagnostische logboek te sturen.

Audit en support: verklaar het waargenomen gedrag

Audit en support: verklaar het waargenomen gedrag — guía visual de DedicatedPHP

Support moet kunnen beantwoorden waarom een klant een flow ziet, niet alleen welke waarde een sleutel heeft. Registreer een beslistrace met tenantidentifier, definitieversie, bron van de effectieve waarde — standaardwaarde of afwijkende waarde —, relevante capaciteiten en evaluatieresultaat. Beperk de toegang tot die informatie en maskeer gevoelige waarden.

Vul deze traceerbaarheid aan met gebruiksstatistieken per optie, validatiefouten, mislukte wijzigingen en opties zonder gebruik. Een configuratie zonder gebruik kan verouderd zijn; een configuratie die lange tijd door slechts één tenant wordt gebruikt, verdient productbeoordeling. Het doel is niet elk verschil te elimineren, maar elk verschil expliciet, controleerbaar en observeerbaar te maken en het te verwijderen wanneer het geen waarde meer toevoegt.

Wil je deze ideeën toepassen op je project?Laten we uw PHP-platform bespreken.
Bekijk gerelateerde service