寬容的讀者

2011 年 5 月 9 日

使用網路服務的好處之一是,它有助於您解耦系統的各個部分。人們可以透過一定程度的分離來處理不同的程式碼庫。雖然您獲得了一些解耦,但您無法完全消除耦合,因為服務仍必須透過其介面彼此通訊。令人遺憾的是,許多團隊讓這種耦合變得比它應該的還要糟。

解耦協作的管理法則應該是 Postel 定律

對您所做的保持保守,對您從他人接受的保持開放。

-- Jon Postel

在協作服務的情況下,最棘手的問題之一是演化。雖然有些人認為您應該先將服務定義弄對,這樣您就永遠不需要變更它們,但我固定的讀者不會驚訝地發現我缺席他們的派對。為了能夠讓服務演化,您需要確保提供者可以進行變更以支援新的需求,同時對其現有客戶造成最小的中斷。

自從撰寫此 bliki 條目以來,Rob Daigneau 在 服務設計模式 中發布了此模式的完整說明

搞砸這件事的常見方法是對服務端點使用某種模式驅動的繫結。一個例子是從 XSD 定義中產生 C# 類別。這被吹捧為一個省時的特性 - 服務提供者發布其服務的 XSD 定義,消費者取得一份副本並產生一個類別。瞧,不用編寫程式。在提供者需要對介面進行任何變更(例如新增欄位)之前,它都能正常運作。像這樣對介面新增欄位不應該是任何人的重大變更 - 但通常會中斷這些架構。

我的建議是在從服務讀取資料時,盡可能地容忍。如果您正在使用 XML 檔案,那麼只取用您需要的元素,忽略任何您不需要的元素。此外,對您正在使用的 XML 結構做出最少的假設。不要使用像 /order-history/order-list/order 這樣的 XPath 搜尋,而是使用 //order。您的目標應該是允許提供者進行任何不應該中斷您的程式碼的變更。一組 XPath 查詢是針對 XML 酬載執行此操作的絕佳方法,但您也可以將相同的原則用於其他事項。

除此之外,請確保只有一段程式碼可以讀取此類資料酬載。 資料傳輸物件 的其中一個目的是將資料酬載包裝在一個方便的物件後面,以便系統的其他部分可以只執行 anOrderHistory.orders,並且不受甚至會中斷容忍讀取器的變更影響。

即使您的資料傳輸協定是二進制的,也值得記住這個原則。想像一下,您在連線的兩端都有 Java 程式,並且想要使用二進制傳輸來縮小您的訊息大小。在這種情況下,大多數人會使用 Java 的內建序列化機制來直接序列化物件,但如果其中一方新增一個欄位,傳輸就會中斷。您可以透過先將資料放入一般性集合(清單和對應)中,然後再序列化這些集合來輕鬆地避免這個問題。如果您在對應中新增一個額外欄位,它仍然會在另一端反序列化,而且容忍讀取器可以輕鬆地忽略它。

為了幫助服務提供者進化他們的服務,您可以傳達您正在讀取的通訊的哪些部分。執行此操作的一個好方法是將讀取器及其測試傳送給他們,以便他們可以在建置過程中使用它們來偵測潛在的中斷。你們有些人可能會將這視為 消費者驅動合約 的下一步

進一步閱讀

服務設計模式 中對此模式有完整的說明。

幾年前,我的同事 Ian Cartwright 發表了一系列關於此主題的有用部落格文章。他指出 架構驗證提供虛假的安全感,而且 序列化有危險,無論是整體而言還是 特別是對於網域物件

Saleem Siddiqui 描述了容忍讀取器如何與 寬宏大量的寫入器 合作無間。