EIP / Loan Broker Example


ローンブローカーのサンプル

はじめに

この章では、ルーティングや変換のパターンを組み合わせて、より大きなソリューションにまとめる方法を示す。

サンプルのシナリオとして選んだのは、複数の銀行からローンの見積もりをとるという手続きのモデリング。 インテグレーションパターンの議論に集中するため、業務プロセスは少し単純化した。

お題

ローンを組もうとしたとき、普通は複数の銀行に見積もりを依頼する。そして、金利が一番有利なところと契約することになる。

見積もり依頼を受けた銀行は、信用調査機関に問い合わせて顧客の信用情報を調査する。 希望する返済期間やこれまでのクレジット記録に基づいて、金利を提示する(あるいは、取引を丁重にお断りする)。 すべての銀行からの見積もりが出そろったら、一番安い金利を出してきたところと契約する。

この流れを示したのが、次の図。

こんな風に複数の銀行に見積もりを依頼するのは面倒なので、そういった作業を肩代わりするのがローンブローカーだ。

顧客はブローカーに見積もり依頼を出す。それを受け取ったブローカーが信用情報を調べて、適切な銀行に見積もり依頼を出す。 銀行からのオファーを受け取ったブローカーは、一番よいものだけを顧客に返す。

この流れを示したのが、次の図。

この処理を設計していこう。


メッセージフローの設計

ローンブローカーに必要なタスクは、次のとおり。

  1. 顧客からのローン見積もりリクエストを受信する。
  2. クレジットのスコアや履歴を信用調査機関から取得する。
  3. 連絡先として最適な銀行を選ぶ。
  4. 選ばれた銀行に、リクエストを送信する。
  5. 各銀行からのレスポンスを受信する。
  6. 一番よいレスポンスを判断する。
  7. そのレスポンスを顧客に戻す。

まずは、ブローカーがどうやってメッセージを受け取るのかというところだが、これについては次の第10章で詳しく扱う。 なので、ここではとりあえず、何らかの方法でメッセージがブローカーに到達するものだとして話を進める。

次に、ブローカーは信用情報を取得しなければいけない。ここではContent Enricherが使える。 連絡先の銀行を決めるタスクも、別のContent Enricherを用意すればいい。

複数の銀行にメッセージを投げて、そのレスポンスを単一のメッセージとして受け取る。 これはまさにScatter-Gatherが適任だ。

Scatter-Gatherがリクエストを送信するときには Publish-Subscribe ChannelもしくはRecipient Listが使える。 戻ってきたメッセージをとりまとめるのに使うのはAggregatorだ。

これらを踏まえると、次の図のような設計になるだろう。

ただ、まだこの時点では、銀行によってリクエストやレスポンスのメッセージフォーマットが異なるということを想定できていない。 ルーティングや集約のロジックは銀行ごとの独自フォーマットから切り離しておきたいので、間に Message Translatorを挟むことになる。 個々のレスポンスを共通フォーマットに変換するには、Normalizerが使える。

これを反映させたのが、次の図。

同期 vs 非同期

メッセージの処理順(Sequencing)については、同期・非同期の二種類の選択肢がある。

  • 同期(シーケンシャル):ブローカーは、ある銀行に見積もりを問い合わせたらそこからの返事を待ち、返事を受け取ってから次の銀行に問い合わせる。
  • 非同期(パラレル):ブローカーは、すべての見積もり依頼を一斉に送信し、返事が戻るのを待つ。

同期型のシーケンス図は、次のようになる。

このソリューションの利点は、シンプルに管理できるということ。並列処理やスレッドは気にせずに済む。 しかし、効率はよろしくない。銀行ごとに別の計算機資源を使っているであろうから、本来はすべてのリクエストを同時に処理できるはず。 なのに、ブローカー側の都合で処理を待たされるので、ユーザーが結果を得るまでにかかる時間は長くなる。

一方、非同期型のシーケンス図は、次のようになる。

このソリューションの利点は、結果を返すまでの時間を短縮できること。 n件の銀行に見積もり依頼を投げたとして、それぞれの銀行での処理時間が同じだと仮定すると、 非同期版で結果を得るまでにかかる時間は同期版のn分の1になる。 ただし、ローンブローカー側での管理が面倒になる。リクエストした順番どおりに結果が返ってくるという保証はないので、 どんな順で答えが返ってきても対応できるようにしておく必要がある。

非同期方式の大きな利点が、もうひとつある。それは、サービスプロバイダーのインスタンスを複数作れるということ。 たとえば信用調査機関のコンポーネントがボトルネックになったとしても、そのコンポーネントを複数走らせればそれで対応できる。 ブローカーはメッセージをキューに送るだけなので、その後実際にメッセージを処理するコンポーネントが何であってもかまわない。

分散 vs 入札

リクエストをどの銀行に配送するのかを指定する方法は、Recipient ListPublish-Subscribe Channelのいずれかを選べる。判断基準は、まず第一に、ローンブローカーが銀行に対してどのくらいの影響力を行使したいのかだ。選択肢は次の三つ。

  • 固定:銀行一覧をハードコーディングする。どのローンリクエストも、同じ銀行群に送られる。
  • 分散:ブローカーが、リクエストごとに最適な銀行群を選ぶ。たとえば、過去のクレジット履歴がお粗末な顧客からのリクエストは、優良顧客に特化した銀行には送らないなど。
  • 入札:ブローカーは、Publish-Subscribe Channelを使ってリクエストをブロードキャストする。リクエストに興味を持つ銀行が、それぞれチャネルを購読する。

どれが正解というわけでもない。さまざまな優先順位や制約に基づいて、どれを採用するかを決めることになる。

「固定」方式は、一番シンプルに管理できる。しかし、銀行の追加や削除がおこるたびに、ブローカー側のリストの更新が必要になる。 また、銀行側にしても、まったく興味がないリクエストを大量に受け取ることになるので、あまりうれしくないだろう。

「分散」方式は、ローンのリクエストごとに、どの銀行に参加させるのかをブローカーが細やかに制御できるようになる。 つまり、リクエストを送る数を効率的に減らせる。さらに、提携関係にある特定の銀行だけを優遇するなどの細工もできる。 ただしこの方式にも弱点がある。ローンブローカーの中に業務ロジックを作り込まなければいけなくなるということだ。

「入札」方式は、リクエストに応えるかどうかを銀行側に決めさせる方式。ローンブローカー側は、ほぼメンテナンス不要になる。 その一方で、銀行側に求められる作業が増える。 Message Filterを使うなりSelective Consumer を実装するなりして自分の欲しいメッセージだけを受け取るのは、銀行側の仕事になる。 ブローカー側のチャネルは、Publish-Subscribe Channelにしなければいけない。 あるいは、別の実装として、Point-to-Point Channelの配列と Recipient Listの組み合わせでPublish-Subscribe Channelをエミュレートする方法もある。

複数チャネル vs 単一チャネル

銀行側からの回答を受け取る方法にも選択肢がある。すべての銀行に、単一の同じレスポンスチャネルを使わせることもできるし、 銀行ごとに個別のレスポンスチャネルを用意することもできる。

単一チャネル方式は、銀行ごとに新たなチャネルを用意する手間を軽減できる。しかし、銀行からのメッセージの中に、その見積もりの発行元を表すフィールドを追加する必要がある。 また、単一チャネル方式の場合、Aggregatorがその入札へのレスポンスの数を知るには、 Recipient Listから別途その情報を受け取らなければいけない(この方式を「初期化済みAggregator」と呼んでいた)。

「入札」方式を使った場合は、レスポンスの数を事前に想定できない。そこでAggregatorは、メッセージの数に依存しない完了条件を設定することになる。


同時並行性

ローンブローカーが同時に複数のリクエストを扱えるようにするには、次の二つの方法がある。

  • 複数のインスタンスを実行する
  • 単一のイベント駆動のインスタンスを実行する

最初の選択肢は、たとえばローンブローカープロセスのプールを用意して、リクエストが来るたびにMessage Dispatcherで空きプロセスを割り当てるなどの手がある。空きがなければキューに入れておけばいい。

ただ、ローンブローカーの処理の大半は外部からの返事を待つことなので、多数のプロセスを並列に動かすのは、リソースの使い方としてあまりよろしくない。 個別のメッセージの処理はシンプルなタスクなので、単一のインスタンスでも十分さばけるだろう。リソースの節約にもなるし、システムもシンプルになる。単一インスタンス方式の弱点は、スケーラビリティに欠けること。なので、大規模なアプリケーションの多くは、この二つの手法を組み合わせて使っている。つまり、複数のプロセスを並行稼働させつつ、それぞれのプロセスが複数のリクエストを同時に処理できるようにするということだ。

三種類の実装

いろんな実装方針のトレードオフを見るために、代表的な三通りの実装を用意してみた。

実装処理順アドレス指定集約方式チャネルタイプ製品・技術
A同期分散チャネル(銀行ごとに個別のチャネルを用意する)Web Service/SOAPJava/Apache Axis
B非同期分散相関ID(すべての銀行が同じチャネルにレスポンスを返す)Message QueueC#/Microsoft MSMQ
C非同期入札相関ID(すべての銀行が同じチャネルにレスポンスを返す)Publish-SubscribeTIBCO Active-Enterprise

担当者のつぶやき

みんなの突っ込み