マルチ層アーキテクチャでバルク操作を実行するときのエラーとフィードバックの処理
-
20-09-2019 - |
質問
多くのオブジェクトで操作を実行できるビジネスロジックメソッドがあるとしましょう。おそらく、リストから選択された人ごとに一度、宝くじ番号を選択してWebサービスを呼び出したいと思うかもしれません。 Javaでは、コードは次のように見えるかもしれません:
Set<Person> selectedPeople = ... // fetch list of people
for ( Person person : selectedPeople ) {
String lotteryNumber = callLotteryNumberWebService( person );
// ...
}
宝くじ番号のWebサービスには、その人が宝くじ番号を要求したこと(おそらくアカウントを請求する)を記録するなど、副作用がある可能性があることに注意してください。この情報(宝くじ番号)は、いくらかのより高いレベル(ビュー)に返還する必要があります。
これが単一の操作が行われた場合、ビジネスロジック法は単一の値(たとえば、宝くじ番号)を返すか、失敗の詳細を例外とスローすることができます。しかし、バルク操作の場合、いくつかの操作が成功し、いくつかの操作が失敗する可能性があります。
これは、多くのアプリケーションで発生する一種の問題のように思えますが、それを処理するためのクリーンな方法があるはずです。したがって、このタイプの情報をビジネスロジックレイヤーからアプリケーション内の別のレイヤーに戻す最良の方法は何ですか(ビューなど)。
解決
私が理解していれば、あなたはいくつかの要求が合格し、いくつかのリクエストが失敗する可能性がある状況があります。エラーがどこに戻ってくるのかはわかりませんが、次のいずれか(またはバリアント、または組み合わせ)を持つことができます。
- 影響を受けるエラーとドメインオブジェクトのリスト。ベースドメインオブジェクトまたは永続的なIDを持つものは、再利用に役立つ場合があります。たとえば、ドメインオブジェクトを参照するエラーのコレクション。
- 何らかのエラーオブジェクト/メッセージの種類のオブジェクトに(AOP、DI)を挿入できます。たとえば、(人。エラー){...}
- ヘッダー、ボディ、エラー情報を使用して、人コレクションをメッセージに包むことができます
- すべてのドメインオブジェクトには、インターフェイスを介してアクセス可能なエラーコレクションを含めることができます。または、人がihaserrorsインターフェイスをサポートします。この汎用を作成し、警告と検証、あらゆる種類のものをサポートするベースエラーオブジェクトを使用できます。
本物のマルチ層(階層化されているのではなく)システムにいる場合は、何らかの一般的なエラー/警告/検証メカニズムに簡単に対応できるメッセージベースのアーキテクチャがある場合があります。 SOA/AJAXシステムはこれに役立ちます。
具体的な質問がある場合は、もう少し深く掘り下げていただきます。
他のヒント
この質問は、例外処理、トランザクション、およびアイデアの適切な用途の重要な違いを強調しています ワークフロー「補償」 正しく述べているときに、質問者が取得しようとしていることです。
これは、多くのアプリケーションで発生する一種の問題のように思えますが、それを処理するためのクリーンな方法があるはずです。
それは一般的な問題であり、最初にあなたが現在試みているトランザクションアプローチのいくつかの背景が次のとおりです。
データトランザクションはもともと二重入力会計の後にモデル化されました - 単一 クレジット そして対応する デビット 一緒に記録する必要がありました。トランザクションがこれよりも大きくなるにつれて、彼らは正しく実装するためにますます問題があり、失敗に対処するのが難しくなります。システムの境界を越えて単一のトランザクションのアイデアを持ち始めたとき、あなたはおそらく間違っていることに近づいています。それは行うことができますが、複雑で、必然的に高層トランザクションコーディネーターが必要です。特定のスケールでは、トランザクションは間違った考え方であり、補償はもっと理にかなっています。
これは、あなたが戻って、ビジネスが実際に行っていることを見るところです。単一の大規模なトランザクションは、ビジネス担当者がそれを見る方法ではない可能性が高いです。通常、彼らはステップが完了し、その後の結果に応じて、補償するさまざまなアクションが必要になる場合があります。これは、ワークフローのアイデアです 補償 入って来る。 これらの概念の紹介の1つは次のとおりです
たとえば、Amazonから本を注文した場合、ショッピングカートにいる間にレコードを「ロック」しないか、厳格な取引を使用して、注文が確認されたときに本がまだ在庫があるかどうかを判断することさえありません。彼らはとにかくあなたにそれを販売し、できる限りそれを出荷します。彼らが数週間以内にそれを在庫に入れることができなかったなら、彼らはおそらくあなたのニーズを満たそうとしていることを伝えるメールをあなたに送るでしょう、そしてあなたは彼らがそれを在庫に入れるのを待ち続けることができます、またはあなたはあなたですご注文をキャンセルできます。これは補償と呼ばれ、多くの現実世界のビジネスプロセスで必要です。
最後に、あります 例外的なものはありません これのいずれかについて。これが起こり、通常の制御フローを使用する可能性があることを期待してください。ここで言語の例外機能を使用してはいけません(例外をスローする時期のための良いルール)。また、サービスの実装内で発生する例外を見たり取り扱ったりするためのツール固有の(WCF?)メカニズムに依存する必要もありません。通信障害は、データ契約(障害契約)の通常の部分である必要があります。
残念ながら、「それを処理するためのクリーンな方法」には、魔法のように世話をする旗はありません。問題を分解し続け、結果のすべてのピースに対処し続けなければなりません。うまくいけば、これらの概念が、この問題に対処する際に他の人がしたこととあなたを結びつけることを願っています。
概要:
- あなたの問題は、トランザクションの概念に大きくなりました - >ワークフロー補償を調べます。
幸運を -
オブジェクトを識別するカスタムメイドのエラーオブジェクトのコレクションを返したいと思います。これは、エラー、エラーコード、および説明によって行われます。このようにして、エラーを試してみると、ユーザーにさらに表示または表示できます。
あなたがこれらの用語で考えているなら、あなたは本当に過度の例外だと思います!
例外をスローするのではなく、失敗を意味する値を返すことは完全に問題ありません。多くの場合、それはより良いです。例外は、抽象化のレベルで回復できない場合に使用するのが最適ですが、コントロールフローの主な手段としてそれらを使用したり、プログラムを読みにくくしてはいけません。
Webサービスは例外を返さず、返されるコードとメッセージを返します。返品された情報を提示する有用な表現を保存し、ビューまたはそれを見ているもののためにそれらのリストを返します。
理想的には、Webサービスへの呼び出しは次のようになるはずです。
List<Person> selectedPeople = ... //fetch list of people
callLotteryNumberWebService(selectedPeople.ToArray );
各人にWebサービスを呼び出すことは費用がかかります。サーバーの端では、リストを反復し、操作を実行する必要があります。サーバーサイドコードは、2つの例外をスローできます。BulkoperationFailedException -DBダウンまたは構成ファイルが欠落しているために致命的なエラーがある場合。さらなる処理は不可能です。 BulkoperationException-これには、人に関する例外の配列が含まれています。各オブジェクトを一意に参照するために、いくつかのIDを持続できます。あなたのコードは次のようになります:
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
操作は、例外が投げられない場合に成功したとみなされます。ユーザー定義の例外を使用する代わりに、障害のカスタムエラーコードを使用できます。サーバー側でbulkoperationexceptionを構築するのは難しいです。次に、サーバー側からBulkoperationFailedExceptionおよびBulkoperationExceptionにスローされたエラーを分類する必要があります。これは私が私のプロジェクトの1つで処理した方法でした
私は調べます DTO この種のタスクのために。 DTOには、持続が成功したかどうか、および他の種類の「メタデータ」が含まれているかどうかに関する情報も含めることができます。
おそらくタイプの結果マップを返します Map<Person,Future<String>>
私から getLotteryNumbers<Collection<Person>>
サービス。
その後、マップを繰り返して使用します Future.get()
宝くじ番号または例外のいずれかを取得します。
私のサービスのいくつかでは、すべての呼び出しを単一のアイテム呼び出しとして実装し、サービスにロジックをバッチアップしてグループとして処理するのが好きです。バッチングはaを使用して実装されます LinkedBlockingQueue
そして投票スレッド。
このシナリオでは、私はaを返します Future<Thing>
バッチの結果が利用可能になるのを待っています CountdownLatch
.
実際のJavaの同時性を見て、これらのコンポーネントがすべて一緒に機能する方法を確認してください http://jcip.net/
特に高スループットシステムの別の方法は、処理エンティティがオブジェクト上で操作を実行するキューに基づいて設計を使用し、結果に基づいて他のエンティティによる追加処理のためにオブジェクトを異なるキューに置き、次に進むことです。 。これにより、追加の処理が必要になるために発生するボトルネックが減少します。