Gestire errori e feedback durante l'esecuzione di operazioni in blocco in un'architettura a più livelli

StackOverflow https://stackoverflow.com/questions/1340062

Domanda

Supponiamo che tu abbia un metodo di logica aziendale in grado di eseguire alcune operazioni su una serie di oggetti.Forse vuoi chiamare un servizio web per la raccolta dei numeri della lotteria, una volta per ogni persona selezionata da un elenco.In Java, il codice potrebbe assomigliare a questo:

Set<Person> selectedPeople = ... // fetch list of people
for ( Person person : selectedPeople ) {
    String lotteryNumber = callLotteryNumberWebService( person );
    // ...
}

Tieni presente che il servizio web del numero della lotteria può avere effetti collaterali, come la registrazione che la persona ha richiesto un numero della lotteria (magari addebitando il suo account), quindi anche se la chiamata al servizio web fallisce per una persona, potrebbe essere riuscita per altri.Queste informazioni (i numeri della lotteria) dovranno essere restituite a un livello superiore (la visualizzazione).

Se si verificasse una singola operazione, il metodo della logica aziendale potrebbe restituire un singolo valore (ad esempio, il numero della lotteria) o generare un'eccezione con tutti i dettagli dell'errore.Ma per le operazioni di massa, sarebbe possibile che alcune operazioni riuscissero e altre fallissero.

Questo sembra un tipo di problema che potrebbe verificarsi in molte applicazioni e dovrebbe esserci un modo pulito per gestirlo.Quindi, qual è il modo migliore per riportare questo tipo di informazioni dal livello della logica aziendale a un altro livello in un'applicazione (come una vista), preferibilmente in un modo generico che possa essere riutilizzato per diversi tipi di dati e operazioni?

È stato utile?

Soluzione

Se ho capito, hai una situazione in cui alcune richieste possono passare e altre possono fallire.Non sono sicuro di dove vuoi che ritorni l'errore, ma potresti avere uno dei seguenti (o una variante o una combinazione):

  • Un elenco degli errori e degli oggetti del dominio interessati.Un oggetto di dominio di base o qualcosa con un ID persistente potrebbe essere utile per il riutilizzo.Per esempio.una raccolta di errori riferiti agli oggetti del dominio.
  • Potresti iniettare (AOP, DI) nell'oggetto Persona una sorta di oggetto/messaggio di errore.Per esempio.se (persona.Errori){...}
  • Potresti racchiudere la raccolta Person in un messaggio con intestazione, corpo e informazioni sull'errore
  • Tutti gli oggetti del tuo dominio potrebbero includere una raccolta di errori accessibile tramite un'interfaccia;o Person supporta l'interfaccia IHasErrors.Potresti renderlo generico e utilizzare un oggetto Error di base che supporti avvisi, convalida e ogni sorta di cose.

Se ti trovi in ​​un vero sistema a più livelli (piuttosto che a strati), potresti avere un'architettura basata su messaggi che potrebbe facilmente ospitare qualche tipo di meccanismo generico di errore/avviso/convalida.I sistemi SOA/Ajax si prestano a questo.

Felice di approfondire un po' se hai qualche domanda specifica.

Altri suggerimenti

Questa domanda evidenzia importanti differenze tra gli usi appropriati della gestione delle eccezioni, delle transazioni e dell'idea flusso di lavoro "compensazione" Questo è ciò che il richiedente sta cercando di ottenere, quando si afferma correttamente:

Questo sembra un tipo di problema che si verificherebbe in molte applicazioni e dovrebbe esserci un modo pulito per gestirlo.

È un problema comune, prima alcuni background sull'approccio transazionale che stai attualmente tentando:

Le transazioni di dati sono state originariamente modellate dopo la contabilità a doppia entrata-una singola credito e un corrispondente addebito doveva essere registrato insieme o per niente. Man mano che le transazioni diventano più grandi di questo, diventano sempre più problematiche da implementare correttamente e più difficili da affrontare un fallimento. Quando inizi a portare l'idea di una singola transazione attraverso i confini del sistema, molto probabilmente ti stai avvicinando a esso. Può essere fatto, ma richiede coordinatori di transazioni complessi e necessariamente più elevati. A una certa scala le transazioni sono la mentalità sbagliata e la compensazione inizia a avere molto più senso.

È qui che torni indietro e guardi cosa fa realmente l'azienda. Molto probabilmente una singola transazione di grandi dimensioni non è il modo in cui gli uomini d'affari lo vedono. Di solito vedono un passo completato e, a seconda dei risultati successivi, potrebbero essere necessarie diverse azioni da compensare. Qui è dove l'idea di un flusso di lavoro e compensazione entra. Ecco un'introduzione a quei concetti

Ad esempio, se ordini un libro da Amazon, probabilmente non "bloccano" il record mentre si trova nel carrello della spesa o persino usano transazioni rigorose per determinare se il libro è ancora in stock quando l'ordine è confermato. Te lo venderà comunque e lo spediranno quando possono. Se non sono riusciti a prenderlo in st può annullare il tuo ordine. Questo si chiama compensazione ed è necessario in molti processi aziendali del mondo reale.

Finalmente c'è niente di eccezionale su tutto ciò. Aspettatevi che ciò possa accadere e usa il normale flusso di controllo. Non dovresti usare le funzionalità di gestione delle eccezioni della tua lingua qui (buone regole per quando lanciare un'eccezione). Né dovresti fare affidamento su meccanismi specifici per lo strumento (WCF?) Per vedere o gestire le eccezioni che si verificano all'interno dell'implementazione del servizio. La comunicazione dei guasti dovrebbe essere una parte normale del contratto di dati (contratti di guasti).

Sfortunatamente, per "pulire la via per gestirlo" non esiste una bandiera da impostare magicamente, devi continuare a scomporre il problema e affrontare tutti i pezzi risultanti. Spero che questi concetti ti collegheranno a ciò che le altre persone hanno fatto quando hanno a che fare con questo problema.

Riepilogo:

  • Il tuo problema ha superato il concetto di transazione -> esamina la compensazione del flusso di lavoro.

Buona fortuna -

Preferirei restituire una raccolta di oggetti di errore su misura che identificano l'oggetto, che viene effettuato dall'errore, dal codice di errore e dalla descrizione. In questo modo gli errori possono essere provati a risolvere o visualizzare ulteriormente l'utente.

Penso che tu stia davvero usando eccezioni se stai pensando in questi termini!

È perfettamente OK restituire valori che significano fallimento, piuttosto che lanciare un'eccezione. Spesso è meglio. Le eccezioni vengono utilizzate al meglio quando non puoi recuperare a livello di astrazione in cui ti trovi, ma non dovresti usarle come mezzo principale per il flusso di controllo o i tuoi programmi diventeranno molto difficili da leggere.

Il servizio Web non restituisce eccezioni, restituisce codici e messaggi di ritorno. Vorrei memorizzare alcune utili rappresentazioni che presentano le informazioni restituite e restituirei l'elenco di quelle per la vista o qualunque cosa la guarderà.

Idealmente, la chiamata al tuo servizio web dovrebbe essere così.

List<Person> selectedPeople = ... //fetch list of people
callLotteryNumberWebService(selectedPeople.ToArray );

Effettuare una chiamata di servizio Web per ogni persona è costoso. Alla fine del server, è necessario iterare nell'elenco ed eseguire l'operazione. Il codice lato server può lanciare 2 eccezioni: BulkOPerationFaileDException - Se c'è un errore fatale dovuto a DB Down o Config File mancante. Ulteriore elaborazione non possibile. BulkoperationException - Questo contiene una serie di eccezioni relative a una persona. Puoi persistere un ID per fare riferimento in modo univoco a ciascun oggetto. Il tuo codice sarà così:

List<Person> selectedPeople = ... // fetch list of people 

try{
    callLotteryNumberWebService(selectedPeople.ToArray);
}catch  (BulkOperationFailedException e) {
    SOP("Some config file missing/db down.No person records processed")
}catch(BulkOperationException e)  {
    UserDefinedExceptions us =  e.getExceptions()
    foreach(exception ein us)   {
        // read unique id to find which person object failed
    }
}

construct msg based on which personobject succeeded and which failed

L'operazione è considerata di successo quando non vengono lanciate eccezioni. È possibile disporre di codici di errore personalizzati per gli errori invece di utilizzare le eccezioni definite dall'utente. Costruire BulkoperationException nel lato server è difficile. In secondo luogo, è necessario classificare gli errori lanciati dal lato server in BulkOPerationFaileDException e BulkoperationException. È così che avevo gestito in uno dei miei progetti

Esaminerei Dtos Per questo tipo di compito. Il DTO potrebbe anche includere informazioni sul fatto che la persistenza fosse di successo o no e altri tipi di "metadati".

Probabilmente restituirei una mappa dei risultati del tipo Map<Person,Future<String>> dal mio getLotteryNumbers<Collection<Person>> servizio.

Quindi itegherei attraverso la mappa e userei il Future.get() Per ottenere il numero della lotteria o l'eccezione.

In alcuni dei miei servizi mi piace implementare tutte le chiamate come chiamate singoli e quindi avere una logica nel mio servizio per raggrupparle ed elaborarle come gruppo. Il batching è implementato utilizzando un LinkedBlockingQueue e un filo di polling.

In questo scenario restituisco un file Future<Thing> che attende che i risultati batch siano disponibili utilizzando a CountdownLatch.

Dai un'occhiata alla concorrenza di Java in pratica per vedere come questi componenti possono lavorare tutti insieme http://jcip.net/

Un altro modo, soprattutto per i sistemi di throughput ad alto contenuto, sarebbe quello di utilizzare un progetto basato su code in cui un'entità di elaborazione eseguirebbe operazioni su un oggetto e quindi in base ai risultati mettere l'oggetto in diverse code per ulteriori elaborazioni da parte di altre entità e quindi andare avanti . Ciò ridurrebbe i colli di bottiglia che si verificherebbero a causa di ulteriori elaborazioni che sarebbero richieste elaborazioni dell'ordine per i prodotti esauriti

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top