脈絡:前面的模式說明了如何建構訊息、如何把它路由到正確的目的地。但在許多情況下,企業整合方案是在既有應用程式之間路由訊息——遺留系統、套裝應用、自製應用,或由外部夥伴營運的系統。
這些應用程式各自圍繞著專有的資料模型打造:
- 每個應用程式對「客戶」這個實體的認知都略有不同——定義客戶的屬性是哪些、客戶又與哪些其他實體有關聯
- 會計系統可能更在意客戶的稅籍編號,而 CRM 系統存的是電話與地址
- 應用程式底層的資料模型通常直接決定了實體資料庫 schema、介面檔案格式或 API——而這些正是整合方案必須介接的東西
結果是:應用程式期待收到的訊息,長得像它自己的內部資料格式。
除了各應用程式的專有模型與格式外,整合方案往往還要與力求獨立於特定應用的標準資料格式打交道,例如 RosettaNet、ebXML、OAGIS 以及各種產業專屬聯盟所定義的協定。許多情況下,整合方案必須用「官方」格式與外部各方溝通,而內部系統卻建立在專有格式上。
為什麼不乾脆統一格式#
如果能修改所有應用程式改用共同資料格式,就不必轉換訊息了。但這在多個層面上都很困難:
- 改資料格式風險高、難度大,而且需要大量修改內在的業務功能。對多數遺留應用程式而言,改資料格式在經濟上根本不可行。
還記得 Y2K 改造工程嗎?那次變更的範圍不過就是一個欄位的長度而已。
- 就算欄位名稱與資料型別統一了,實體表示法仍可能天差地遠——一個用 XML 文件,另一個用 COBOL copybook。
- 調整一方去遷就另一方,反而把兩者綁得更緊。 企業整合的關鍵架構原則之一是鬆散耦合(見 Canonical Data Model),而修改 A 去配合 B 的資料格式,會讓兩者直接相依於彼此的內部表示法——這就沒辦法在不影響另一方的前提下替換或修改其中一方,而這種需求在企業整合中相當常見。
- 把格式轉換直接做進 Message Endpoint? 這樣所有應用程式都會以共同格式收發訊息,而非各自的內部格式。但這需要存取端點程式碼,對套裝應用而言通常辦不到;而且把格式轉換寫死在端點裡,也會減少程式碼重用的機會。
解法#
Message Translator 是 [GoF] 中 Adapter 模式在訊息傳遞領域的對應物——轉接器把一個元件的介面轉成另一種介面,好讓它能在不同情境下被使用。

圖 3-5:Message Translator 解法示意
轉換的四個層次#
訊息轉換可能發生在數個不同層次。有時資料元素名稱與型別相同,只是表示法不同(XML 檔 vs. 逗號分隔值 vs. 固定長度欄位);有時全都用 XML,卻採用不同的標籤名稱。
借用 OSI 參考模型的精神,可以把轉換分成四層:
| 層次 | 處理的東西 | 轉換需求(範例) | 工具/技術 |
|---|---|---|---|
| 資料結構(應用層) | 實體、關聯、基數 | 把多對多關係壓成聚合 | 結構對應模式、自製程式碼 |
| 資料型別 | 欄位名稱、資料型別、值域、限制、代碼值 | 郵遞區號從數值轉字串;把 first name 與 last name 串成單一 name 欄位;把美國州名換成兩字母代碼 | EAI 視覺化轉換編輯器、XSL、資料庫查表、自製程式碼 |
| 資料表示 | 資料格式(XML、名值對、固定長度欄位、EAI 廠商格式)、字元集(ASCII、Unicode、EBCDIC)、加密/壓縮 | 解析資料表示並以另一種格式輸出;視需要加解密 | XML 剖析器、EAI 剖析/輸出工具、自製 API |
| 傳輸 | 通訊協定:TCP/IP sockets、http、SOAP、JMS、TIBCO RendezVous | 在不影響訊息內容的前提下跨協定搬運資料 | Channel Adapter、EAI 轉接器 |
逐層說明#
- 傳輸層位於堆疊底部,負責在不同系統之間完整且可靠地傳輸資料,處理跨網段的封包遺失與其他網路錯誤。有些 EAI 廠商提供自家傳輸協定(如 TIBCO RendezVous),其他整合技術則沿用 TCP/IP 協定(如 SOAP)。不同傳輸層之間的轉換由 Channel Adapter 提供。
- 資料表示層也稱「語法層」,定義被傳輸資料的表示法。之所以需要這層轉換,是因為傳輸層只能搬運字元或位元組流,複雜資料結構必須先轉成字串。常見格式包含 XML、固定長度欄位(如 EDI 紀錄)或專有格式;資料也常被壓縮或加密、帶有檢查碼或數位憑證。要介接表示法不同的系統,資料可能得先解密、解壓縮、剖析,再輸出成新格式,可能還要重新壓縮與加密。
- 資料型別層定義應用(領域)模型所依據的資料型別。這裡處理的是:日期欄位要存成字串還是原生日期結構?日期是否帶時間?基於哪個時區?
Postal Code只放美國 ZIP,還是也能放加拿大郵遞區號?若是美國 ZIP,要不要含 ZIP+4?是否必填?存成一欄還是兩欄?這些問題通常在**資料字典(Data Dictionary)**中處理。
資料型別的議題遠不只「這欄是字串還是整數」。
想像依區域組織的銷售資料:某部門的應用程式把國家分成四區——西部、中部、南部、東部,以
W、C、S、E標示;另一個部門卻把太平洋區與山區分開、東北與東南也分開,每區以唯一的兩位數字標示。那麼字母E對應到哪個數字?
- 資料結構層在應用領域模型的層次描述資料,因此也叫應用層。它定義應用程式處理的邏輯實體(Customer、Address、Account)以及它們之間的關係:一位客戶能有多個帳戶嗎?能有多個地址嗎?客戶之間能共用地址嗎?多位客戶能共用一個帳戶嗎?地址屬於帳戶還是客戶?——這是實體關係圖與類別圖的領域。
解耦的層級#
整合中的許多設計取捨,都由「讓元件或應用程式解耦」這個需求所驅動。解耦是管理變更的必要工具:整合連接的是既有應用程式,而且必須容納它們的變化。
- Message Channel 讓應用程式不必知道彼此的位置。
- Message Router 甚至讓應用程式不必對共同的路由達成共識。
- 但只要它們仍相依於彼此的資料格式,這種解耦所能達成的獨立性就很有限——Message Translator 能移除這一層相依。
串接轉換#
許多商業情境需要跨多層的轉換。
例如一筆以固定格式檔案呈現的 EDI 850 採購訂單,必須轉成 XML 文件、透過 http 送到訂單管理系統,而該系統對 Order 物件的定義又不一樣。這個轉換橫跨全部四層:傳輸從檔案變成 HTTP、資料表示從固定格式變成 XML、資料型別與結構都得轉換以符合訂單管理系統定義的 Order 物件。
分層模型的美妙之處在於:我們可以只處理某一層而不管底下各層,因而能選擇在不同的抽象層次上工作。
用 Pipes and Filters 串接多個 Message Translator,好處是每一層各一個轉譯器,這些元件都能在其他情境中重用——例如 Channel Adapter 與 EDI-to-XML 轉譯器可以通用到能處理任何進站的 EDI 文件。
這種作法也讓個別層次可以互換:你可以沿用同一套結構轉換機制,只把「資料表示轉換」那一環抽換掉,就從輸出固定格式改成輸出逗號分隔檔。
相關的特化與變體#
Message Translator 有許多特化與變體:
- Content Enricher——擴充訊息內的資訊
- Content Filter——移除訊息內的資訊
- Claim Check——移除資訊,但存起來供日後取回
- Normalizer——把多種不同的訊息格式轉成一致的格式
- Canonical Data Model——說明如何運用多個 Message Translator 達成資料格式的解耦
- Messaging Bridge——透過連接多套訊息系統,執行傳輸層的轉換
上述每個模式內部都可能發生複雜的結構轉換,例如把多對多關係對應成一對一關係。
範例:用 XSL 做結構轉換
轉換的需求如此普遍,以致 W3C 定義了轉換 XML 文件的標準語言 XSL。XSL 的一部分是 XSLT——一種以規則為基礎、把 XML 文件轉成另一種格式的語言。
假設有一份進站 XML 文件要送給會計系統。若兩邊都用 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>這份文件裝的是客戶資料:每位客戶可關聯多個地址,每個地址可含多個電話號碼。XML 把地址表示成獨立實體,因此多位客戶可以共用同一個地址。
會計系統需要的表示法則是(如果你覺得德文標籤名很牽強,別忘了最熱門的企業軟體之一正是以德文欄位名聞名):
<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 建立在模式比對上,對習慣程序式程式設計的人來說可能有點難讀。簡單說:每當進站文件中的元素符合 match 屬性所指定的運算式,對應的 <xsl:template> 就被呼叫。例如 <xsl:template match="customer"> 會讓後續幾行對來源文件中的每個 <customer> 元素各執行一次;接著把 first 與 last name 串起來輸出到 <Name>。
取地址稍微麻煩些:XSL 先查出正確的 <address> 元素實例,再呼叫「副程式」getaddr;getaddr 從該 <address> 抽出地址與電話——有手機號碼就用手機,否則用家用電話。
範例:視覺化轉換工具

圖 3-6:以拖放方式建立轉換(BizTalk Mapper)
如果你覺得 XSL 有點難懂,你並不孤單。因此多數整合廠商提供視覺化轉換編輯器:畫面左右兩側分別顯示兩種文件格式的結構,使用者在兩側之間拖放來建立元素對應,這比手寫 XSL 簡單得多。也有廠商完全專攻轉換工具,例如 Contivo, Inc.。
整合進 Visual Studio 的 Microsoft BizTalk Mapper 就是一例:圖表比 XSL 腳本更清楚地呈現個別元素之間的對應,但部分細節(例如地址是怎麼挑出來的)被藏在 functoid 圖示底下。
能拖放做轉換,大幅縮短了開發 Message Translator 的學習曲線。但一如既往,視覺化工具在除錯或建構複雜方案時也可能變成負擔——因此許多工具讓你能在 XSL 與視覺化工具之間來回切換。