Quando un’applicazione riceve un dato ambiguo, forzarlo in una risposta «valido» o «non valido» spesso nasconde il problema invece di risolverlo. Può accadere con moduli, integrazioni o risultati proposti da un sistema automatizzato: mancano informazioni contestuali per accettare o scartare il record in sicurezza. In questi casi, la revisione umana dei dati incerti in PHP deve essere uno stato del dominio, con regole esplicite, non un’etichetta informale né una coda improvvisata.
La progettazione deve rispondere a quattro domande: che cosa è stato ricevuto, che cosa viene proposto, chi deve decidere e che cosa accade dopo. Separare queste responsabilità facilita la gestione del flusso, la correzione degli errori e la spiegazione del motivo per cui un dato è stato elaborato in un determinato modo.
Classifica il problema prima di creare gli stati

Non tutti i dati che non possono proseguire richiedono una revisione umana. Distingui almeno queste situazioni:
- Non valido: viola una regola verificabile, per esempio un formato che non può essere interpretato. A seconda dell’origine e del prodotto, è opportuno rifiutarlo o chiederne la correzione.
- Incompleto: manca un campo necessario. Si può richiedere il dato, conservare la bozza o attendere un’ulteriore fonte.
- Incerto: sono possibili più interpretazioni oppure le prove non consentono di decidere automaticamente. Potrebbe essere necessaria la valutazione di una persona.
La revisione non deve diventare la destinazione generica di ogni errore. Se un valore viola un vincolo inequivocabile, inviarlo a una coda di revisione aggiunge soltanto lavoro. Quando invece la decisione dipende dal contesto o dal giudizio, uno stato soggetto a revisione consente di interrompere l’elaborazione senza dichiarare prematuramente il dato corretto o errato.
È inoltre opportuno distinguere questo flusso da una quarantena di importazione. La quarantena isola in genere gli input che non devono essere integrati nel sistema, per esempio perché non hanno superato i controlli tecnici o di sicurezza. La revisione risolve un’ambiguità di business. Uno stesso record può passare attraverso entrambi i meccanismi, ma i loro obiettivi, permessi e criteri di uscita non sono intercambiabili.
Definisci un ciclo di vita essenziale ed esplicito
Inizia dagli stati necessari per descrivere il processo, non da un elenco di tutte le possibilità future. Un ciclo di base potrebbe essere:
pending_review: in attesa di una decisione.confirmed: una persona conferma un valore utilizzabile.rejected: la persona stabilisce che il dato non deve essere elaborato.reopenedo una nuova voce in revisione: una decisione precedente viene rimessa in discussione.
Definisci le transizioni consentite. Per esempio, un caso in attesa può essere confermato o rifiutato; un caso già risolto può essere riaperto solo a determinate condizioni autorizzate. Non consentire a una richiesta qualsiasi di cambiare direttamente lo stato impostandolo su un valore arbitrario. Una transizione deve specificare lo stato di origine, quello di destinazione, l’attore autorizzato e gli effetti associati.
Registra il motivo della decisione e l’identità del responsabile. Se un dato viene rifiutato, annota una motivazione utile, non soltanto un valore booleano. Se viene confermata un’interpretazione, conserva il valore approvato e, se pertinente, le prove su cui si è basata la decisione. Stabilisci se il motivo è obbligatorio a seconda dell’azione ed evita di includere informazioni personali non necessarie nelle note libere.
Separa input, proposta e valore confermato
Una progettazione robusta non sovrascrive il dato ricevuto con l’interpretazione di un processo automatico o con la correzione di una persona. Mantieni distinti questi concetti:
- Dato ricevuto: l’input originale e la sua provenienza, con adeguati controlli di accesso e conservazione.
- Interpretazione proposta: un suggerimento generato da regole, un’integrazione o un sistema assistito, insieme alle informazioni necessarie per valutarlo.
- Valore confermato: il valore utilizzato dall’applicazione dopo la decisione autorizzata.
Questa separazione impedisce che una proposta venga trattata accidentalmente come una verità e consente di ricostruire come si è arrivati al valore attuale. Non è necessario duplicare indiscriminatamente ogni campo: conserva i dati necessari per audit e supporto, applicando le policy di conservazione e protezione dei dati. Se una proposta include un punteggio o una spiegazione automatizzata, trattali come contesto, non come decisione.
Registra le modifiche come eventi o voci dello storico non modificabili dall’applicazione ordinaria: identificativo del caso, azione, stato precedente e nuovo, attore, data e motivo. Il modello può inoltre mantenere lo stato corrente per consultare facilmente la coda. La correzione si rappresenta con una nuova decisione o una riapertura collegata allo storico; cancellare la decisione precedente distrugge la tracciabilità.
Implementa le decisioni nel livello di dominio
In PHP, incapsula le transizioni in un servizio applicativo o in un’entità di dominio, invece di affidarti a ogni controller affinché applichi le stesse regole. Un esempio schematico:
final class ReviewDecisionService
{
public function confirm(ReviewCase $case, User $actor, string $value, string $reason): void
{
if (!$actor->canReview($case)) {
throw new DomainException('Accesso non consentito');
}
if (!$case->isPending()) {
throw new DomainException('Il caso non è in attesa');
}
$case->confirm($value, $actor->id(), $reason);
}
}
Il frammento illustra dove collocare le regole, non costituisce un’implementazione completa. Convalida anche il formato e i limiti del valore, verifica che l’attore sia autorizzato per quel caso e registra la decisione. Non confondere l’autenticazione — chi è l’utente — con l’autorizzazione — che cosa può fare su quel record. Se esistono ruoli o separazione delle funzioni, esprimi tali vincoli come regole del prodotto.
Quando l’aggiornamento del caso e la scrittura dello storico devono essere coerenti, salvali in una transazione del database. Se la decisione attiva effetti esterni, come una chiamata a un’altra API, non presumere che la transazione locale includa anche quel sistema. Usa un meccanismo di consegna affidabile, come una transactional outbox quando l’architettura lo richiede, e progetta i consumer in modo che tollerino i retry.
Progetta una coda che aiuti a decidere
Una coda utile consente di assegnare priorità e trovare i casi senza nasconderne il contesto. Includi filtri pertinenti — stato, data, tipo o assegnazione — e mostra soltanto le informazioni necessarie per comprendere il caso. Se è presente una proposta, indica chiaramente che si tratta di una proposta. Mostra la provenienza, il motivo della revisione e le azioni disponibili, evitando di presentare controlli che l’utente non è autorizzato a utilizzare.
L’assegnazione può ridurre le duplicazioni del lavoro, ma non sostituisce l’autorizzazione né la protezione dalle decisioni simultanee. Una persona può aprire un caso e un’altra risolverlo prima che la prima invii la propria risposta. Per impedire che una schermata non aggiornata sovrascriva una modifica recente, usa il controllo ottimistico della concorrenza: al salvataggio confronta una versione o un timestamp e rifiuta l’operazione obsoleta, chiedendo all’utente di ricaricare il caso.
Se il processo richiede prenotazioni temporanee, definiscine la scadenza e stabilisci che cosa accade quando scadono. Non usare un’assegnazione permanente come blocco involontario. Registra le azioni rilevanti per poter spiegare chi ha esaminato il caso e individuare i record che restano in attesa più a lungo di quanto sia accettabile per l’operatività.
Correggi e riapri senza cancellare il passato
Una decisione può essere errata o cambiare perché sono emerse nuove prove. Definisci chi può riaprirla, quale motivo deve fornire e se il dato confermato non è più disponibile durante una nuova revisione. La risposta dipende dall’impatto del valore sui processi successivi; per esempio, potrebbe essere necessario interrompere nuove operazioni, ma non eliminare automaticamente gli effetti già prodotti.
Conserva la decisione precedente nello storico e crea una nuova versione o un nuovo evento di revisione. Se la correzione del dato richiede di ricalcolare informazioni dipendenti, identifica esplicitamente tali effetti e definisci una procedura idempotente per evitare duplicati. Una correzione dello stato non deve fingere che un’operazione esterna sia già stata annullata.
Verifica regole, permessi ed effetti successivi

I test devono coprire l’intero flusso e i relativi limiti, non soltanto verificare che il modulo venga salvato. Come minimo, controlla che:
- Un caso in attesa consenta le transizioni previste e rifiuti le altre.
- Gli attori non autorizzati non possano decidere, riaprire né consultare dati soggetti a restrizioni.
- Ogni decisione conservi l’attore, il motivo e i valori precedenti e nuovi, secondo la policy definita.
- Due modifiche simultanee non consentano a una decisione obsoleta di sovrascriverne un’altra.
- Un errore nella registrazione dello storico non lasci lo stato aggiornato senza tracciabilità.
- Ripetere un effetto successivo non duplichi operazioni né notifiche.
Come lista di controllo della progettazione, verifica che l’incertezza abbia una causa identificabile; che il dato originale, la proposta e il valore confermato siano distinti; che ogni transizione abbia un permesso e un motivo; che la coda fornisca un contesto sufficiente; e che la riapertura conservi lo storico e definisca l’impatto operativo. Se uno di questi punti dipende da convenzioni informali, manca ancora una regola di sistema.



