ハブは送り先を表で決める
一言でいうと
ハブは、ヘッダーの取引コードと受信機関だけを見て宛先を決めます。その判断はコードではなくデータ(ルーティング表)に置き、ルールが重なったときにどれが勝つかを文書で明記し、どこにも送れない電文は握りつぶさず即座にエラーレスポンスとして返します。
なぜ必要なのか
モジュール1で見たとおり、システム同士を直接つなぐポイントツーポイント接続は、システム数が増えると線が2乗で増えます。ハブ・アンド・スポーク構造はすべての線をハブ1つにまとめ、その代わりハブに新しい仕事を1つ任せます。それがこの電文を誰に渡すかという仕事です。HohpeとWoolfの『Enterprise Integration Patterns』は、この役割をContent-Based Routerと呼びます。メッセージ内のデータ(フィールドがあるか、特定のフィールドの値が何か)を見て、送り先のチャネルを選ぶ構成要素です。このコースのハブは、そのうちヘッダーの2つのフィールドだけを見ます。
同じ文書が警告していることがあります。ルーターは、宛先が変わるたびに直さなければならない頻繁な保守ポイントになりやすく、ルールが複雑なら、設定可能なルール集合で宛先を計算する形を選べる、というものです。銀行のハブでは、この警告がそのまま現実になります。取引コードは毎月新しく生まれ、システムが移行されると数十個のコードの宛先が一度に変わります。ルーティングをif tx_code == "BKTR0001"のようなコードで書いておくと、取引を1つ追加するたびにハブを再デプロイしなければならず、ハブのデプロイはその銀行のすべての取引を一瞬ずつ揺さぶります。そこでルーティングルールは表(データ)に切り出し、ハブのコードは「表を読んで優先順位どおりに選ぶ」ことだけを行います。表が変わることは多く、ルールの解釈方法が変わることはまれです。よく変わるものとまれにしか変わらないものを切り離しておくわけです。
どう動くのか
元になるのは、業務部門のインターフェース一覧です。どの取引がどのシステムへ行くかは、開発者ではなく業務担当者がExcelで管理していることが多くあります。この一覧には、廃止されたインターフェース、開発中のインターフェース、同じ取引の古いバージョンが混ざっています。ハブが使うルーティング表は、これを整理した結果です。状態が運用の行だけを載せ、同じ取引コードが複数あれば最大のバージョン1つだけを残します。ここでよくある落とし穴が、バージョンの比較です。Excelから書き出したCSVのバージョンは文字なので、文字列として比較すると"9"が"10"より大きくなります。整数に変換して比較しなければなりません。
表は「どこへ」だけでなく「どのように」も載せます。ルーティング表の1行には、宛先とともに、同期・非同期の区分とタイムアウトがあります。タイムアウトが取引ごとに違うのには理由があります。残高照会は数秒以内に答えてこそ意味がありますが、保険請求書類の提出は数十秒かかるのが正常です。すべての取引を1つの値にまとめると、短くすれば請求がすべて失敗に見え、長くすれば止まった勘定系を待つあいだにハブの接続が溜まります。タイムアウトは取引の性質なので、取引コードと同じ行に置きます。
ルールは4つあり、順位があります。このコースの架空の基準(ROUTING.md)は次のように定めています。
| 順位 | ルール | 条件 | なぜこの順位なのか |
|---|---|---|---|
| 1 | ORG | 受信機関が自行ではない → FEP | 他の機関へ出ていく電文は、外部回線と機関別の形式が集まる1つの出口からだけ出る必要があります |
| 2 | EXACT | 取引コードが完全一致 | 例外は最も具体的なルールで書きます |
| 3 | PREFIX | 最も長いプレフィックスが一致 | CD*で業務群全体をまとめつつ、CDLN*のような下位のまとまりが勝ちます |
| 4 | NONE | どれにも一致しない → E101 | 推測して送りません |
「具体的なものが勝つ」という発想は、IPルーティングと同じです。RFC 1812は、ルーターが経路を選ぶとき、特定ホストの経路を先に、次にネットワークのプレフィックスをプレフィックスが長い順に選ぶと記しています。CD*とCDLN*の両方に一致するなら、長いほうがより多くのことを知っているルールです。
優先順位は、表の行の順序であってはなりません。「上から最初に一致した行」で実装すると、行の順序が隠れたルールになります。誰かがExcelを取引コード順に並べ替えて書き出し直しただけで、CD*がCDLN*より上に来て、カードローンの電文が黙ってカード系へ行ってしまいます。エラーは何も出ません。そのため優先順位は、コードが明示的に計算します。完全一致を先に探し、プレフィックスは長さで並べ替えます。
ルーティングできないものはレスポンスで返します。表にない取引コードを受け取ったハブが、ログだけ残して電文を捨てると、送ったチャネルはレスポンスを待ってタイムアウトになって初めて失敗を知ります。そのあいだ顧客の画面は回り続け、チャネルが再送まですると、同じ無駄足が2回起きます。ハブは受け取ったリクエストを裏返し、応答コードE101を載せたレスポンス電文を即座に作ります(モジュール1のレスポンスの作り方と同じで、取引コードとGUIDを維持し、機関を入れ替えます)。一方、形式が誤った電文(E102)には、このレスポンスを作れません。ヘッダー自体が信用できないので、GUIDも信用できないからです。
決定のたびに監査ログを残します。「この取引はなぜ情報系へ行ったのか」という問いに答えるには、宛先だけでなくどのルールに一致したか(EXACT・PREFIX・ORG・NONE)とGUIDが必要です。ログは追記だけを行います。上書きされる監査ログは、監査ログではありません。
現場での姿
最もよくある事故は、広いプレフィックスが新しい取引を飲み込んでしまうことです。カード系へ行くCD*ルールがある状態で、勘定系へ行くべき新しい取引CDLN0010ができたのに、表に完全一致の行を入れ忘れると、その取引はエラーなしでカード系へ行きます。カード系は知らない取引なので拒否し、障害対応の会議はカード系を先に疑います。監査ログにPREFIXが記録されていれば、原因が1行で見えます。完全一致を期待していた取引がプレフィックスで出ていったこと自体が、シグナルです。
2つ目は、廃止されたインターフェースが表に残ることです。システムを移行しても古い行を消さないと、しばらくは誰もそのコードで送らないので静かです。ある日誰かがそのコードを再利用すると、廃止されたシステムへトラフィックが流れます。状態列を設け、「運用だけを載せる」を機械的に守る理由です。3つ目は、このモジュールのバージョン比較の落とし穴のような、一覧を整理するスクリプトの些細なミスです。ルーティング表を作ったあとは、元の一覧と照合するテストを一度回す必要があります。
次のラボですること
業務部門の雑然としたインターフェース一覧を整理してルーティング表routes.csvを作り、ルーターrouter.pyを1ステップずつ育てます。完全一致、プレフィックス(長いほうが勝つ)、機関に基づくルール、電文ヘッダーによるルーティング、E101レスポンス、監査ログの順です。採点ツールは毎回、新しい取引コードと宛先名でランダムな表を作ってルーターを動かすため、ルールをコードに埋め込むと合格できません。最後に受信箱の30件を振り分けます。