EIP / Message Translator


Message Translator(メッセージトランスレーター)

一言要約

要するに、GoFのAdapterパターン[1][2]のメッセージング版。

Context

エンタープライズインテグレーションでは、既存アプリケーション間(レガシーシステムとかパッケージソフトとか内製アプリとか外注先が運用するシステムとか)でのメッセージのやりとりが発生する。これらの多くはプロプライエタリなデータモデルを使っており、外部から受け取るメッセージも当然そのモデルに合わせた形式であることを期待する。

また、それに加えて、アプリケーション間でのデータ交換用の標準フォーマットとかいうものもいろいろ存在する(RosettaNet?、ebXML、OAGIS、…)。インテグレーションソリューションでは、これらの「公式な」フォーマットでのデータ交換を要求されることがある。


Problem

異なるデータフォーマットを採用するシステム間での通信にメッセージングを使うには?

Force

全アプリケーションに手を加えて、すべて共通のデータフォーマットを使うことにすれば、無駄な心配はしなくて済む。けど、それは難しい(Shared Databaseパターンを参照)。 あと、アプリケーションのデータフォーマットを変更するのって、かなりリスキーだし手間がかかるよ。 Y2K対応にどれだけの労力を要したか、みんな覚えてるよね?あれって、たった1個のフィールドのサイズを変更するだけの話だったんだよ!!!

データ項目名とか型を仮に統一できたとしても、それだけでは不十分だ。たとえば、一方のアプリケーションはデータをXMLで扱っているのに、もう一方のアプリケーションはCOBOLコピーブックを使ってるとかいうこともある。

そんな困難も乗り越えて、無事データフォーマットを統一できたとしよう。だとしても、それはそれで問題だ。 だって、各アプリケーションが密結合になってしまうから。エンタープライズインテグレーションのアーキテクチャ設計で大切なのは、 アプリケーション間を疎結合にすること(Canonical Data Modelパターンを参照)。 データフォーマットを統一してしまうと、どちらか一方のアプリケーションを改修したりリプレイスしたりするときに、もう一方にも手を入れないといけなくなる。 改修とかリプレイスって、決して珍しくないことだよね?

データフォーマットの変換ロジックをMessage Endpointに組み込むという手もあるけど、そのためにはエンドポイントのコードを自由にさわれないといけない。パッケージアプリの場合、そんなことはできないね。あと、フォーマット変換ロジックをエンドポイントにハードコードすると、コードの再利用性が……。

Solution

特殊なフィルターであるメッセージトランスレーターを他のフィルターあるいはアプリケーションとの間に挟み、データフォーマットの変換をする。

このパターンはいわゆるAdapterで、あるコンポーネントのインターフェイスをもう一方のインターフェイスにあわせて変換し、別のコンテキストで使えるようにする。

Levels of Transformation

メッセージの変換とひとことで言ってもいろんな内容が含まれるので、OSI参照モデルの考え方をちょこっと拝借してレイヤーに分けてみた。

トランスポート層
システム間でのデータ転送の方法を定義する。パケットのロストやネットワーク障害への対応などもこの層で行う。EAIベンダー自前のプロトコル(TIBCO RendezVous?など)や、TCP/IPを使ったインテグレーション技術(SOAPなど)がこの層にあてはまる。トランスポート層でそれぞれ異なる仕組みを使っているシステム間でのデータ転送にはChannel Adapterパターンを使う。
データリプレゼンテーション層
システム層と呼ばれることもある。ここでは転送するデータの表現方法(XMLにする?固定長フィールドにする?など)を定義する。データの圧縮や暗号化、チェックサムやデジタル署名の追加をするのもこの層。
データタイプ層
ドメインモデルで利用するデータ型を定義する。「日付フィールドを文字列型にする?日付型にする?」「曜日を含める?含めない?」「タイムゾーンは?」など。
データストラクチャー層
アプリケーション層と呼ばれることもある。ここでは、アプリケーションで扱う論理エンティティ(顧客・住所・アカウントなど)、そしてエンティティ間の関連(一対多・多対多など)を定義する。E-R図とかクラス図でカバーする領域。

Levels of Decoupling

インテグレーションの設計時に出てくるトレードオフは、コンポーネントやアプリケーションを疎結合にせねば!という観点からのものであることが多い。変更に強くするためには疎結合にすることが欠かせない。Message Channels?を使えば結合相手の居場所を知らなくてもよくなるし、Message Routerを使えば共通のMessage Channels?に関する合意すら不要になる。しかし、こんな小技で切り離せる結合なんてたかが知れている。結合相手のデータフォーマットに依存しているうちはまだまだだね。そこでメッセージトランスレーターですよ。こいつを使えば、お互いの依存をさらに減らせるようになるよ。

Changing Transformations

多くのビジネスシナリオは複数のレイヤーで変換を要する。
ここで、EDI 850 Purchase Orderレコードを考えてみよう。

  • HTTPを通して送られる。
  • 固定フォーマットのファイルである。
  • XML文書へ変換される。
  • 異なる受注オブジェクトの定義を使用する受注管理システムへ送られる。

要求される変換は、4つのレベルすべてにわたる。

  • トランスポートはファイル転送をHTTPに。
  • データフォーマットは固定フォーマットからXMLに。
  • データタイプとデータフォーマットは受注管理システムで定義された受注オブジェクトに対応するように。
    • P.89の図では下位から、Transport、Data Representation、Data Types、Data Structuresとなってるんだけど?(高江洲)
レイヤーモデルの美しさは、下位のレイヤーを意識することなくひとつのレイヤーを扱うことができるので、
一度に1つの抽象レベルに焦点を当てられるということ。


Pipes and Filtersを使って複数のMessage Translatorをチェーンすると、P.90の図のようなアーキテクチャとなる。
各々のレイヤでMessage Translatorを作成すると、他のシナリオでもこれらのコンポーネントを再利用できるよ。


複数のMessage Translatorをチェーンするのは、他のレイヤーに影響を与えずに個々のレイヤーの変換方法を変えることだってできる。

  • 固定フォーマットへの変換処理の代わりにCSVにコンバートできたり。


Message Translatorのパターンは、多くの特殊化されたものやバリエーションあるよ。

  • Envelope Wrapperは、メッセージングシステムの向こう側へ転送されるよう、メッセージデータをエンベロープ内にラップ。
  • Content Enricherは、情報を増大化。
  • Content Filterは、情報を削除。
  • Claim Checkは、情報を削除するが後で取り出せるよう保存。
  • Normalizerは、ことなるメッセージフォーマットを統一フォーマットにコンバート。
  • Canonical Data Modelは、データフォーマットの依存を減らすのに、複数のMessage Translatorをどう扱うかを示す。

これらのパターン内で複雑な構造変化が起こりうるのだ。

  • 多対多の関係を1対1の関係にマッピングするなど。

Examples

Structural Transformation with XSL

XMLの変換にはXSL。

  • XSLT言語はルールベースの言語でXML文書を他のフォーマットに変換。


この本は統合に関する物でXSLTの本じゃないので、血みどろの詳細を知りたければXSLTの仕様を参照してね。
ということで単純な例を。


入力データとしてXML文書があり、それを会計システムに渡す必要があると仮定。

  • 両方のシステムはXMLを使用。
  • Data Representationレイヤーは同一。
  • フィールド名、データの型、構造がいくつか異なってる。

入ってくるのはこんなん。

<data>
  <customer>
    <firstname>Joe</firstname>
    <lastname>Doe</lastname>
    <address type="primary">
      <ref id="55355"/>
    </address>
    <address type="secondary">
      <ref id="77889"/>
    </address>
  </customer>
  <address id="55355">
    <street>123 Main</street>
    <city>San Francisco</city>
    <state>CA</state>
    <postalcode>94123</postalcode>
    <country>USA</country>
    <phone type="cell">
      <area>415</area>
      <prefix>555</prefix>
      <number>1234</number>
    </phone>
    <phone type="home">
      <area>415</area>
      <prefix>555</prefix>
      <number>5678</number>
    </phone>
  </address>
  <address id="77889">
    <company>ThoughtWorks</company>
    <street>410 Townsend</street>
    <city>San Francisco</city>
    <state>CA</state>
    <postalcode>94107</postalcode>
    <country>USA</country>
  </address>
</data>

これは顧客データを含んでる。

  • 複数のアドレスを持てる。
  • アドレスは複数の電話番号を持てる。
  • 複数の顧客がアドレスを共有できるよう、アドレスは独立したエンティティとして表現される。


一方、会計システムが必要としてるのは次のような感じで。

  • ドイツ語のタグに違和感を感じるのであれば、ある最も人気のエンタープライズソフトウェアの一部は、ドイツ語のフィールド名で有名なことを知っておくことだね!
    • って何だろ?(高江洲)
<Kunde>
  <Name>Joe Doe</Name>
  <Adresse>
    <Strasse>123 Main</Strasse>
    <Ort>San Francisco</Ort>
    <Telefon>415-555-1234</Telefon>
  </Adresse>
</Kunde>

これは非常にシンプル。

  • タグ名が異なる。
  • いくつかのフィールドがひとつに統合される。

この変換に使うXSLTはこれ。

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes"/>
  <xsl:key name="addrlookup" match="/data/address" use="@id"/>
  <xsl:template match="data">
    <xsl:apply-templates select="customer"/>
  </xsl:template>
  <xsl:template match="customer">
    <Kunde>
      <Name>
        <xsl:value-of select="concat(firstname, ' ', lastname)"/>
      </Name>
      <Adresse>
        <xsl:variable name="id" select="./address[@type='primary']/ref/@id"/>
        <xsl:call-template name="getaddr">
          <xsl:with-param name="addr" select="key('addrlookup', $id)"/>
        </xsl:call-template>
      </Adresse>
    </Kunde>
  </xsl:template>
  <xsl:template name="getaddr">
    <xsl:param name="addr"/>
    <Strasse>
      <xsl:value-of select="$addr/street"/>
    </Strasse>
    <Ort>
      <xsl:value-of select="$addr/city"/>
    </Ort>
    <Telefon>
      <xsl:choose>
        <xsl:when test="$addr/phone[@type='cell']">
          <xsl:apply-templates select="$addr/phone[@type='cell']" mode="getphone"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:apply-templates select="$addr/phone[@type='home']" mode="getphone"/>
        </xsl:otherwise>
      </xsl:choose>
    </Telefon>
  </xsl:template>
  <xsl:template match="phone" mode="getphone">
    <xsl:value-of select="concat(area, '-', prefix, '-', number)"/>
  </xsl:template>
  <xsl:template match="*"/>
</xsl:stylesheet>

XSLはパターンマッチに基づくけど、手続き的なプログラミングに慣れてるならちょっと読むのにいやかもね。

  • 簡単に説明すると、XML文書でマッチしたところがあれば、<xsl:template>の指示に従って処理されるということ。
    • <xsl:template match="customer">とあれば、<customer>が引っかかると。

Nameはファーストネームとラストネームをつなげてだすだけ。
アドレスはちとトリッキー。

  • <address>要素を探してgetaddrサブルーチンを呼び出す。
  • getaddrはオリジナルの<address>からアドレスと電話番号を抽出する。
  • 携帯電話があればそれを、なければ家の電話を使用。

Visual Transformation Tools

XSLプログラミングが少し不可解だったとしても、気にしない気にしない。
たいていの統合ベンダーは左右それぞれに二つのドキュメントを表示した、ビジュアル変換エディタを提供しているのだ。

  • ユーザーはそれを使って、要素をつなぐことで関連づけられる。
  • XSLのコーディングなんかよりずっとシンプル!
  • Contivoなんかの一部のベンダーは、完全に変換ツールに特化。

P.94の図は、VisualStudio?に統合されたMicrosoft BizTalk? Mapperエディタ。
XSLのスクリプトなんかに比べると、はるかにわかりやすいマッピング。
アドレスの選択などの詳細はFunctoidアイコンと呼ばれるもので隠蔽されてる。

ドラッグドロップができると、Message Translatorの学習カーブをかなり縮めることができる。
ってもよくあることだが、デバッギングや複雑なソリューションに対しては不利な面も。
なので、多くのツールはXSLとビジュアルな表現とを行き来できるようになっている。

担当者のつぶやき

  • Y2K対応とか懐かしい話題が ^^; 出版当時は、まだ記憶に新しいころだったんでしょうね。(高木)
  • 難しい内容ではなかったので結構さくさく。(高江洲)

みんなの突っ込み