Supponiamo che sia possibile utilizzare un metodo API per calcolare la somma di tutti gli ordini effettuati da un cliente specifico:
Amount CalculateOrderSum(int customerId)
{
// Perform authentication to make sure caller has access to customerId
// Retrieve customer with id customerId
// Retrieve all orders related to the customer
// Retrieve details for different orders (not always, depending on state)
}
In qualsiasi punto di questa funzione, alcuni amministratori di sistema possono eliminare vecchi elementi dal sistema. Ciò significa che uno dei seguenti può accadere mentre il metodo sopra è in esecuzione:
- L'autenticazione fallirà perché il cliente non esiste più
- Il cliente non può essere caricato, poiché è stato eliminato
- Il cliente può essere caricato, ma dopo un millesimo di secondo gli ordini vengono cancellati e non possono essere recuperati.
- Il recupero dei dettagli dell'ordine funziona per alcuni ordini, ma non ha esito positivo per gli altri poiché sono stati eliminati durante l'elaborazione intermedia.
Desidero che l'applicazione restituisca un messaggio di errore amichevole quando si verifica uno di questi eventi, piuttosto che restituire una NullReferenceException o simile.
Come vedo, ci sono alcuni approcci diversi per aggiungere la gestione degli errori per questa logica:
- Potrei introdurre molti controlli null in tutto il codice, ad esempio: if (customer! = null) lancia OrderRetrievalFailedException ("customer is a goner."). Dal momento che tutti i dati nel database possono essere eliminati in qualsiasi momento, ciò comporterebbe un notevole dispendio di se stessi in tutto il codice (che sembra essere disordinato)
- È possibile modificare la funzionalità di eliminazione per contrassegnare i clienti o gli ordini come eliminati (anziché rimuovere effettivamente le righe del database). In questo modo, la funzione potrebbe ancora svolgere il proprio lavoro perché i dati saranno ancora lì. Il problema qui è che in realtà vogliamo eliminare i vecchi dati per diversi motivi (ad esempio, considerazioni meno sulla superficie di attacco e sulle prestazioni).
- Potrei cambiare tutti i metodi da lanciare se non è possibile caricare un oggetto (così GetOrders (customerId) potrebbe lanciare CustomerNotFoundException se il cliente non può essere caricato) che verrebbe catturato nella funzione CalculateOrderSum e un errore dato all'utente. Quindi in pratica il codice dovrebbe essere disseminato di if (qualcosa == null) per lanciare una nuova SomeException.
- Potrei introdurre un meccanismo di blocco globale, in modo che un cliente non possa essere cancellato mentre qualcuno sta leggendo uno qualsiasi dei suoi dati. Il problema qui è che il nostro sistema è distribuito, quindi avremmo bisogno di implementare un meccanismo di blocco centrale. Inoltre, ho una brutta esperienza con il blocco delle righe del database in casi d'uso come questo nel database ad alto traffico.
Tutti questi approcci sono abbastanza complicati e difficili da ottenere per me, e ciò significa che il "flusso principale di successo" del codice sarà pieno di scenari di eccezione. Mi sto orientando verso l'alternativa 3, ma vorrei sapere se c'è un altro modo "standard e robusto" di gestirlo.
(Sto usando C #, ma presumo che lo stesso problema si applichi agli utenti di Java o C ++ ad esempio)