要するに、GoFのAdapterパターン[1][2]のメッセージング版。
エンタープライズインテグレーションでは、既存アプリケーション間(レガシーシステムとかパッケージソフトとか内製アプリとか外注先が運用するシステムとか)でのメッセージのやりとりが発生する。これらの多くはプロプライエタリなデータモデルを使っており、外部から受け取るメッセージも当然そのモデルに合わせた形式であることを期待する。
また、それに加えて、アプリケーション間でのデータ交換用の標準フォーマットとかいうものもいろいろ存在する(RosettaNet?、ebXML、OAGIS、…)。インテグレーションソリューションでは、これらの「公式な」フォーマットでのデータ交換を要求されることがある。
異なるデータフォーマットを採用するシステム間での通信にメッセージングを使うには?
全アプリケーションに手を加えて、すべて共通のデータフォーマットを使うことにすれば、無駄な心配はしなくて済む。けど、それは難しい(Shared Databaseパターンを参照)。 あと、アプリケーションのデータフォーマットを変更するのって、かなりリスキーだし手間がかかるよ。 Y2K対応にどれだけの労力を要したか、みんな覚えてるよね?あれって、たった1個のフィールドのサイズを変更するだけの話だったんだよ!!!
データ項目名とか型を仮に統一できたとしても、それだけでは不十分だ。たとえば、一方のアプリケーションはデータをXMLで扱っているのに、もう一方のアプリケーションはCOBOLコピーブックを使ってるとかいうこともある。
そんな困難も乗り越えて、無事データフォーマットを統一できたとしよう。だとしても、それはそれで問題だ。 だって、各アプリケーションが密結合になってしまうから。エンタープライズインテグレーションのアーキテクチャ設計で大切なのは、 アプリケーション間を疎結合にすること(Canonical Data Modelパターンを参照)。 データフォーマットを統一してしまうと、どちらか一方のアプリケーションを改修したりリプレイスしたりするときに、もう一方にも手を入れないといけなくなる。 改修とかリプレイスって、決して珍しくないことだよね?
データフォーマットの変換ロジックをMessage Endpointに組み込むという手もあるけど、そのためにはエンドポイントのコードを自由にさわれないといけない。パッケージアプリの場合、そんなことはできないね。あと、フォーマット変換ロジックをエンドポイントにハードコードすると、コードの再利用性が……。
特殊なフィルターであるメッセージトランスレーターを他のフィルターあるいはアプリケーションとの間に挟み、データフォーマットの変換をする。
このパターンはいわゆるAdapterで、あるコンポーネントのインターフェイスをもう一方のインターフェイスにあわせて変換し、別のコンテキストで使えるようにする。
メッセージの変換とひとことで言ってもいろんな内容が含まれるので、OSI参照モデルの考え方をちょこっと拝借してレイヤーに分けてみた。
インテグレーションの設計時に出てくるトレードオフは、コンポーネントやアプリケーションを疎結合にせねば!という観点からのものであることが多い。変更に強くするためには疎結合にすることが欠かせない。Message Channels?を使えば結合相手の居場所を知らなくてもよくなるし、Message Routerを使えば共通のMessage Channels?に関する合意すら不要になる。しかし、こんな小技で切り離せる結合なんてたかが知れている。結合相手のデータフォーマットに依存しているうちはまだまだだね。そこでメッセージトランスレーターですよ。こいつを使えば、お互いの依存をさらに減らせるようになるよ。
多くのビジネスシナリオは複数のレイヤーで変換を要する。
ここで、EDI 850 Purchase Orderレコードを考えてみよう。
要求される変換は、4つのレベルすべてにわたる。
レイヤーモデルの美しさは、下位のレイヤーを意識することなくひとつのレイヤーを扱うことができるので、 一度に1つの抽象レベルに焦点を当てられるということ。
Pipes and Filtersを使って複数のMessage Translatorをチェーンすると、P.90の図のようなアーキテクチャとなる。
各々のレイヤでMessage Translatorを作成すると、他のシナリオでもこれらのコンポーネントを再利用できるよ。
複数のMessage Translatorをチェーンするのは、他のレイヤーに影響を与えずに個々のレイヤーの変換方法を変えることだってできる。
Message Translatorのパターンは、多くの特殊化されたものやバリエーションあるよ。
これらのパターン内で複雑な構造変化が起こりうるのだ。
XMLの変換にはXSL。
この本は統合に関する物でXSLTの本じゃないので、血みどろの詳細を知りたければXSLTの仕様を参照してね。
ということで単純な例を。
入力データとしてXML文書があり、それを会計システムに渡す必要があると仮定。
入ってくるのはこんなん。
<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はパターンマッチに基づくけど、手続き的なプログラミングに慣れてるならちょっと読むのにいやかもね。
Nameはファーストネームとラストネームをつなげてだすだけ。
アドレスはちとトリッキー。
XSLプログラミングが少し不可解だったとしても、気にしない気にしない。
たいていの統合ベンダーは左右それぞれに二つのドキュメントを表示した、ビジュアル変換エディタを提供しているのだ。
P.94の図は、VisualStudio?に統合されたMicrosoft BizTalk? Mapperエディタ。
XSLのスクリプトなんかに比べると、はるかにわかりやすいマッピング。
アドレスの選択などの詳細はFunctoidアイコンと呼ばれるもので隠蔽されてる。
ドラッグドロップができると、Message Translatorの学習カーブをかなり縮めることができる。
ってもよくあることだが、デバッギングや複雑なソリューションに対しては不利な面も。
なので、多くのツールはXSLとビジュアルな表現とを行き来できるようになっている。