Vai al contenuto
DedicatedPHP Contatto

Configurazione per cliente in un SaaS PHP senza creare varianti difficili da gestire

Progetta una configurazione per cliente governata in un SaaS PHP per rispondere alle esigenze B2B senza moltiplicare biforcazioni né condizionali.

Diagramma editoriale di un SaaS PHP con configurazione centralizzata per cliente, permessi, capacità e processi asincroni

Una richiesta commerciale diventa pericolosa quando smette di essere una decisione esplicita di prodotto e si materializza come un if ($tenantId === ...). All'inizio risolve un'urgenza. Con il tempo, quella condizione compare in controller, template, processi in coda, esportazioni e API. Il risultato non è una configurazione: sono varianti implicite del prodotto, difficili da testare, spiegare e rimuovere.

La configurazione per cliente in SaaS PHP deve consentire differenze deliberate e governate, non preservare ogni eccezione storica. La domanda utile non è «possiamo farlo per questo cliente?», ma «questa variazione rappresenta una dimensione stabile del prodotto di cui potrebbero avere bisogno altri clienti, con regole e supporto sostenibili?».

Il segnale di allarme: un'eccezione permanente nel codice

Il segnale di allarme: un'eccezione permanente nel codice — guía visual de DedicatedPHP

C'è differenza tra adattare un'esperienza e mantenere un ramo nascosto del prodotto. È opportuno intervenire prima che una richiesta specifica crei uno qualsiasi di questi segnali:

  • L'identificatore del tenant, del dominio o del cliente compare nella logica di business.
  • La stessa regola viene replicata nell'interfaccia, nell'API e nel worker asincrono.
  • Il team non sa rispondere quali clienti abbiano un'eccezione né chi l'abbia approvata.
  • Una modifica del piano altera comportamenti funzionali senza una definizione centrale.
  • Rimuovere un adattamento richiede di cercare condizionali in vari repository o servizi.

Un'eccezione può essere legittima durante una fase di discovery o una migrazione, ma deve avere un responsabile, una data di revisione e un esito: trasformarla in una capacità di prodotto, isolarla come integrazione specifica oppure rifiutarla. Lasciarla senza classificazione trasforma il debito tecnico in una promessa commerciale non documentata.

Non confondere configurazione, permessi, capacità e sviluppo specifico

Questi meccanismi rispondono a domande diverse. Mescolarli produce design opachi e regole contraddittorie.

  • Configurazione: definisce come si comporta una funzionalità già esistente per un tenant. Per esempio, il formato di una numerazione, la lingua predefinita o se un flusso richiede un'approvazione aggiuntiva.
  • Permessi: determinano cosa può fare un'identità all'interno di un tenant. Un utente può avere il permesso di approvare pagamenti anche se l'approvazione è configurata come obbligatoria.
  • Capacità: indicano se il tenant ha accesso a una funzionalità o è soggetto a un limite operativo. Possono dipendere da contratto, piano o attivazione controllata, ma non dovrebbero contenere tutta la logica del dominio.
  • Sviluppo specifico: copre un comportamento che non rientra in una dimensione riutilizzabile del prodotto, come un'integrazione con un sistema proprietario del cliente o una trasformazione contrattuale unica.

Una regola pratica aiuta a decidere: se cambia chi esegue un'azione, usi i permessi; se cambia se una funzionalità esiste o è disponibile, usi le capacità; se cambia come opera una funzionalità disponibile, usi la configurazione. Se cambia il modello di business in modo esclusivo, non lo mascheri da flag.

Cosa deve essere configurabile e cosa deve restare nel core

Un'opzione merita di entrare nel catalogo di configurazione quando ha una semantica chiara, un insieme finito di valori, validazioni note e una ragionevole aspettativa di riutilizzo. Richiede anche un'esperienza di supporto comprensibile: qualcuno deve poter spiegare l'effetto della sua modifica senza ispezionare il codice.

Sono in genere buoni candidati i parametri di presentazione, le policy di notifica, le soglie, le sequenze di approvazione, le preferenze regionali e le scelte tra flussi già supportati. Devono invece restare nel core le invarianti di sicurezza, l'integrità dei dati, il calcolo finanziario di base e le regole la cui modifica richiederebbe di reinterpretare entità o contratti esistenti.

Non trasformi dati arbitrari in configurazione solo per flessibilità. Un campo JSON senza schema può nascondere dipendenze impossibili da individuare. Quando un'opzione modifica una regola critica, definisca tipi, valori consentiti, condizioni d'uso e conseguenze sui dati precedenti.

Costruire un modello di configurazione governato

Una chiave isolata non basta. Ogni definizione del catalogo deve includere metadati che consentano di gestire il prodotto in sicurezza:

  • Chiave e descrizione funzionale: nomi stabili, orientati al dominio e non ai dettagli di implementazione.
  • Responsabile: team o figura responsabile che ne decide evoluzione e rimozione.
  • Ambito: globale, tenant, unità organizzativa, progetto o utente. Eviti di consentire tutti gli ambiti per impostazione predefinita.
  • Valore predefinito: comportamento esplicito quando non esiste un override.
  • Tipo e validazione: booleano, enumerazione, numero con intervallo o struttura validata tramite schema.
  • Dipendenze: requisiti rispetto ad altre opzioni, capacità o stato della migrazione.
  • Sensibilità: classificazione dei dati e regole di accesso per lettura e modifica.
  • Ciclo di vita: data di introduzione, revisione, deprecazione e rimozione prevista, quando applicabile.

In PHP, centralizzi la risoluzione in un servizio di dominio, per esempio TenantSettings, e fornisca oggetti tipizzati invece di array privi di contratto. L'applicazione può combinare il valore globale, il valore del tenant e un valore più specifico mediante una precedenza documentata. L'assenza di valore deve risolversi sempre nel valore predefinito, non in un'interpretazione diversa in ogni consumer.

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

Lo storage può essere relazionale o documentale, ma il catalogo e la validazione non dovrebbero dipendere dalla forma di persistenza. Mantenga inoltre uno storico immutabile delle modifiche: valore precedente e nuovo, attore, momento, motivo e canale di modifica. Lo storico non sostituisce un registro di audit delle azioni di business, ma consente di ricostruire quale configurazione fosse in vigore.

Valutare la decisione al confine appropriato

Il problema dei condizionali dispersi non si risolve spostandoli tutti in un controller. Una configurazione che influenza una regola di business deve essere valutata nel servizio o nella policy di dominio che applica tale regola. Il controller traduce la richiesta; il template presenta il risultato; nessuno dei due dovrebbe decidere autonomamente una policy del tenant.

Per comportamenti complessi, usi strategie o policy registrate, invece di catene di booleani. Una policy di fatturazione può selezionare un'implementazione tra modalità supportate dopo aver validato che il tenant disponga della capacità necessaria. In questo modo, l'interfaccia, l'API e la coda invocano la stessa decisione.

I template possono ricevere una vista già preparata, inclusi indicatori di capacità per mostrare o nascondere azioni. Nascondere un pulsante non è autorizzazione. L'API deve applicare permessi, capacità e configurazione sul server, anche se l'interfaccia non espone l'operazione.

Capacità e limiti senza irrigidire i piani

Un piano commerciale può concedere capacità, ma non deve trasformarsi in una raccolta di if ($plan === '...'). Modelli una capacità stabile, come advanced_approvals o api_access, e risolva quali tenant la possiedono mediante una fonte contrattuale o amministrativa. Successivamente, la logica funzionale consulta la capacità, non il nome del piano.

I limiti richiedono una definizione ancora più precisa: cosa viene conteggiato, in quale finestra temporale, quando si applica il blocco e come si comportano retry e processi in coda. Un limite deve essere osservabile e coerente in tutti i punti di ingresso. Se un'integrazione crea risorse al di fuori dell'interfaccia principale, non può eludere lo stesso controllo.

Modificare le impostazioni in modo sicuro e reversibile

Modificare un'opzione può avere effetti immediati su lavori in corso, record esistenti o integrazioni. Prima di salvare, validi tipo, permessi amministrativi, dipendenze e compatibilità con lo stato attuale. Quando l'impatto è rilevante, offra un'anteprima della modifica: quale flusso verrà attivato, quali vincoli viola e quali operazioni future interesserà.

L'attivazione controllata è diversa dal rendere visibile un'opzione nell'intera interfaccia. Può abilitare una capacità per un insieme controllato di tenant e osservarne il comportamento prima di esporla in modo generale. Definisca anche un rollback: quale valore ripristina lo stato precedente, se vi sono migrazioni di dati associate e cosa accade alle operazioni avviate con la nuova configurazione.

Una modifica reversibile nell'interfaccia potrebbe non essere reversibile nei dati. Tratti separatamente entrambe le dimensioni prima di attivare una nuova policy.

Mantenere coerenza in code, API e integrazioni

I processi asincroni introducono una decisione aggiuntiva: risolvere la configurazione durante l'esecuzione del lavoro o conservarne uno snapshot al momento della creazione. Per azioni che devono rispettare la policy in vigore, risolva durante l'esecuzione e includa il tenant nel contesto del lavoro. Per documenti, calcoli o comunicazioni che devono riprodurre la decisione originale, salvi una versione o uno snapshot esplicito insieme al comando.

Non mescoli entrambe le opzioni senza dichiararlo. Un retry può cambiare risultato se consulta una configurazione aggiornata. Definisca idempotenza, versione della configurazione e comportamento atteso nei retry. Le integrazioni esterne necessitano di contratti equivalenti: validazione preventiva, gestione degli errori, limiti e tracciabilità per tenant, senza inviare segreti o dati personali al registro diagnostico.

Audit e supporto: spiegare il comportamento osservato

Audit e supporto: spiegare il comportamento osservato — guía visual de DedicatedPHP

Il supporto deve poter rispondere perché un cliente vede un flusso, non solo quale valore abbia una chiave. Registri una traccia decisionale con identificatore del tenant, versione della definizione, fonte del valore effettivo —predefinito o override—, capacità rilevanti e risultato della valutazione. Limiti l'accesso a queste informazioni e mascheri i valori sensibili.

Completi questa tracciabilità con metriche di utilizzo per opzione, errori di validazione, modifiche non riuscite e opzioni non utilizzate. Una configurazione inutilizzata può essere obsoleta; una usata da un solo tenant per molto tempo merita una revisione di prodotto. L'obiettivo non è eliminare ogni differenza, ma fare in modo che ogni differenza sia esplicita, verificabile, osservabile e rimossa quando smette di apportare valore.

Vuoi applicare queste idee al tuo progetto?Parliamo della tua piattaforma PHP.
Visualizza il servizio correlato