Frage Fehlerbehandlung und Feedback beim Ausführen von Massenoperationen in einer mehrstufigen Architektur


Nehmen wir an, Sie haben eine Business-Logik-Methode, die eine Operation für mehrere Objekte ausführen kann. Vielleicht möchten Sie einen Lotto-Nummer wählen Web-Service, einmal für jede ausgewählte Person aus einer Liste. In Java sieht der Code möglicherweise so aus:

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

Beachten Sie, dass der Lottozahlen-Webservice Nebeneffekte haben kann, wie die Aufzeichnung, dass die Person eine Lottozahl angefordert hat (möglicherweise die Belastung ihres Kontos). Selbst wenn der Webservice-Aufruf für eine Person fehlschlägt, kann dies für andere erfolgreich sein. Diese Information (die Lottozahlen) müssen auf eine höhere Ebene (die Ansicht) zurückgeführt werden.

Wenn dies der Fall wäre, wenn eine einzelne Operation stattfände, könnte die Geschäftslogikmethode einen einzelnen Wert (z. B. die Lottozahl) zurückgeben oder eine Ausnahme mit irgendwelchen Einzelheiten des Fehlers werfen. Bei Massenoperationen ist es jedoch möglich, dass einige der Operationen erfolgreich sind und einige fehlschlagen.

Dies scheint eine Art von Problem zu sein, das in vielen Anwendungen auftreten würde und es sollte einen sauberen Weg geben, damit umzugehen. Also, was ist der beste Weg, um diese Art von Informationen von der Business-Logik-Ebene zu einer anderen Schicht in einer Anwendung (wie eine Ansicht), vorzugsweise in einer generischen Art und Weise, die für verschiedene Arten von Daten und Operationen wiederverwendet werden können?


11
2017-08-27 10:01


Ursprung


Antworten:


Wenn ich verstehe, haben Sie eine Situation, in der einige Anforderungen bestehen und einige fehlschlagen können. Ich bin mir nicht sicher, wo der Fehler zurückkommen soll, aber Sie könnten eine der folgenden (oder eine Variante oder eine Kombination) haben:

  • Eine Liste der Fehler und der betroffenen Domänenobjekte. Ein Basisdomänenobjekt oder etwas mit einer persistenten ID könnte für die Wiederverwendung nützlich sein. Z.B. eine Sammlung von Fehlern, die sich auf Domänenobjekte beziehen.
  • Sie könnten (AOP, DI) in das Person-Objekt eine Art Fehlerobjekt / Nachricht einfügen. Z.B. if (Person. Fehler) {...}
  • Sie könnten die Personenkollektion in eine Nachricht mit Header-, Body- und Fehlerinformationen einfügen
  • Alle Ihre Domänenobjekte können eine Fehlersammlung enthalten, auf die über eine Schnittstelle zugegriffen werden kann. oder Person unterstützt die IHasErrors-Schnittstelle. Sie könnten dies generisch machen und ein Basis-Fehlerobjekt verwenden, das Warnungen und Validierung und alle möglichen Dinge unterstützt.

Wenn Sie sich in einem echten mehrschichtigen System (statt eines geschichteten Systems) befinden, verfügen Sie möglicherweise über eine nachrichtenbasierte Architektur, die leicht einem generischen Fehler- / Warnungs- / Validierungsmechanismus gerecht wird. Hierfür bieten sich SOA / Ajax-Systeme an.

Gerne, wenn Sie ein paar spezielle Fragen haben.


1
2017-09-07 06:00



Diese Frage hebt wichtige Unterschiede zwischen der Verwendung von Ausnahmebehandlung, Transaktionen und der Idee hervor Workflow "Kompensation" Darauf versucht der Fragesteller zu kommen, wenn er richtig feststellt:

Dies scheint eine Art von Problem zu sein, das in vielen Anwendungen auftreten würde und es sollte einen sauberen Weg geben, damit umzugehen.

Es ist ein häufiges Problem, zuerst einige Hintergrundinformationen über den Transaktionsansatz, den Sie gerade versuchen:

Datentransaktionen wurden ursprünglich nach der doppelten Buchführung modelliert - eine einzige Kredit und eine entsprechende Lastschrift musste zusammen oder überhaupt nicht aufgenommen werden. Wenn Transaktionen größer werden, wird es immer problematischer, sie korrekt zu implementieren, und es ist schwieriger, mit einem Fehler umzugehen. Wenn Sie beginnen, die Idee einer einzelnen Transaktion über Systemgrenzen hinaus zu tragen, nähern Sie sich höchstwahrscheinlich dem Fehler. Es kann getan werden, erfordert aber komplexe und notwendigerweise höhere Latenz-Transaktionskoordinatoren. In einem bestimmten Umfang sind Transaktionen die falsche Einstellung und die Kompensation beginnt viel mehr Sinn zu machen.

Hier gehen Sie zurück und schauen, was das Geschäft eigentlich macht. Eine einzelne große Transaktion ist höchstwahrscheinlich nicht die Art, wie die Geschäftsleute sie sehen. Normalerweise sehen sie, dass ein Schritt abgeschlossen ist, und abhängig von den nachfolgenden Ergebnissen können verschiedene Aktionen zum Kompensieren erforderlich sein. Hier ist die Idee eines Workflows und Vergütung kommt herein. Hier ist eine Einführung in diese Konzepte 

Wenn Sie beispielsweise ein Buch bei Amazon bestellen, "sperren" Sie den Datensatz wahrscheinlich nicht, während er sich in Ihrem Einkaufswagen befindet, oder verwenden Sie strikte Transaktionen, um festzustellen, ob das Buch bei der Bestätigung der Bestellung noch im Lager ist. Sie werden es Ihnen sowieso verkaufen und es versenden, wenn sie können. Wenn sie es nicht geschafft haben, es innerhalb weniger Wochen auf Lager zu bekommen, werden sie Ihnen wahrscheinlich eine E-Mail schicken, die Ihnen sagt, dass sie versuchen, Ihre Bedürfnisse zu befriedigen, und Sie warten können, bis sie es auf Lager haben oder Sie kann Ihre Bestellung stornieren. Dies wird als Kompensation bezeichnet und ist in vielen realen Geschäftsprozessen erforderlich.

Endlich gibt es nichts Außergewöhnliches über irgendetwas davon. Erwarten Sie, dass dies passieren kann und verwenden Sie den normalen Kontrollfluss. Sie sollten die Ausnahmebehandlungsfunktionen Ihrer Sprache hier nicht verwenden (gute Regeln für das Auslösen einer Ausnahme). Sie sollten sich auch nicht auf werkzeugspezifische (WCF?) Mechanismen verlassen, um Ausnahmen innerhalb der Service-Implementierung zu erkennen oder zu behandeln. Kommunizierende Fehler sollten ein normaler Bestandteil Ihres Datenvertrages sein (Fehlerverträge).

Leider, durch "saubere Art und Weise, es zu handhaben" gibt es keine Flagge zu setzen, die sich magisch darum kümmern wird, Sie müssen das Problem weiter zerlegen und mit all den resultierenden Stücken fertig werden. Hoffentlich werden diese Konzepte Sie mit dem verbinden, was andere Leute im Umgang mit diesem Thema getan haben.

Zusammenfassung:

  • Ihr Problem ist dem Konzept einer Transaktion entwachsen -> schauen Sie sich die Workflow-Kompensation an.

Viel Glück -


10
2017-09-01 18:56



Ich würde es vorziehen, eine Sammlung von benutzerdefinierten Fehlerobjekten zurückzugeben, die das Objekt identifizieren, was durch den Fehler, den Fehlercode und die Beschreibung bewirkt wird. Auf diese Weise kann versucht werden, die Fehler zu beheben oder dem Benutzer weiter anzuzeigen.


1
2017-08-27 10:06



Ich denke, dass Sie wirklich über Ausnahmen ausnutzen, wenn Sie in diesen Begriffen denken!

Es ist vollkommen in Ordnung, Werte zurückzugeben, die einen Fehler bedeuten, anstatt eine Ausnahme auszulösen. Oft ist es besser. Ausnahmen werden am besten verwendet, wenn Sie auf der Abstraktionsebene, auf der Sie sich befinden, nicht wiederhergestellt werden können, aber Sie sollten sie nicht als Hauptmittel des Kontrollflusses verwenden oder Ihre Programme werden sehr schwer zu lesen sein.

Der Webdienst gibt keine Ausnahmen zurück, er gibt Rückkehrcodes und Nachrichten zurück. Ich würde eine nützliche Repräsentation speichern, die die zurückgegebenen Informationen präsentiert und die Liste derjenigen zurückgibt, die für die Ansicht oder was auch immer angezeigt wird.


1
2017-08-31 17:16



Ich würde nachsehen DTOs für diese Art von Aufgabe. Der DTO könnte auch Informationen darüber enthalten, ob die Persistenz erfolgreich war oder nicht und andere Arten von "Metadaten".


0
2017-08-31 13:43



Ich würde wahrscheinlich eine Ergebniskarte vom Typ zurückgeben Map<Person,Future<String>> Von meinem getLotteryNumbers<Collection<Person>> Bedienung.

Ich würde dann durch die Karte iterieren und die verwenden Future.get() um entweder die Lottozahl oder die Ausnahme zu erhalten.

In einigen meiner Dienste implementiere ich gerne alle Anrufe als Einzelanrufe und verfüge dann über Logik in meinem Dienst, um sie als Gruppe zu verarbeiten und zu verarbeiten. Die Stapelverarbeitung wird mit a implementiert LinkedBlockingQueue und ein Umfrage-Thread.

In diesem Szenario gebe ich ein Future<Thing> die darauf wartet, dass die Stapelergebnisse mit a verfügbar sind CountdownLatch.

Sehen Sie sich Java Concurrency in der Praxis an, um zu sehen, wie diese Komponenten zusammenarbeiten können http://jcip.net/ 


0
2017-08-31 10:34



Ein anderer Weg, insbesondere für Systeme mit hohem Durchsatz, bestünde darin, einen auf Warteschlangen basierenden Entwurf zu verwenden, bei dem eine Verarbeitungsentität Operationen an einem Objekt ausführt und dann das Objekt basierend auf den Ergebnissen in andere Warteschlangen für die zusätzliche Verarbeitung durch andere Entitäten stellt und dann weitergeht . Dies würde Engpässe reduzieren, die aufgrund einer zusätzlichen Verarbeitung auftreten würden, die z.B. Auftragsabwicklung für nicht verarbeitete Produkte


0
2017-09-02 09:27



Im Idealfall sollte der Aufruf an Ihren Web-Service so sein.

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

Einen Web-Service-Anruf für jede Person zu tätigen, ist kostspielig. Auf der Server-Seite müssen Sie über die Liste iterieren und die Operation ausführen. Der serverseitige Code kann 2 Ausnahmen auslösen: BulkOperationFailedException - wenn ein schwerwiegender Fehler aufgrund einer fehlenden Datenbank oder Konfigurationsdatei vorliegt. Weiterverarbeitung nicht möglich. BulkOperationException - enthält eine Reihe von Ausnahmen, die sich auf eine Person beziehen. Sie können eine ID beibehalten, um sich eindeutig auf jedes Objekt zu beziehen. Dein Code wird wie folgt aussehen:

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

Die Operation gilt als erfolgreich, wenn keine Ausnahmen ausgelöst werden. Sie können benutzerdefinierte Fehlercodes für Fehler haben, anstatt benutzerdefinierte Ausnahmen zu verwenden. Das Konstruieren der BulkOperationException auf der Serverseite ist schwierig. Zweitens müssen Sie Fehler, die von der Serverseite in BulkOperationFailedException und BulkOperationException ausgelöst werden, kategorisieren. So hatte ich in einem meiner Projekte behandelt


0
2017-09-07 08:43