前言
建構完整的架構邊界是一件很耗成本的事情,你必須為系統設計雙向的Boundary介面、Input和Output資料結構,以及設計所有相關的依賴關係管理,以便將系統分割成可獨立編譯和部屬的元件,這需要大量的前期開發成本與後期維護成本。
好的架構師即使覺得這樣的成本太高,但考量日後可能還是會需要這些邊界,所以他們還是會希望能留下這些邊界。
這種預防性設計在敏捷社群中為人詬病,因為它明顯違反YAGNI:「You Aren't Going to Need it.(你並不需要它)」,這時候,架構師就會需要引入不完全邊界(partial boundary)的概念了。
整理: 簡單說,就是在保留邊界的情況下,透過以下的方式來省下一些成本。
省下最後一步
建構不完全邊界的一種方式就是在將系統分割成一系列可以獨立編譯、部屬的元件後,在將它們放在同一個元件之中做編譯和部屬,雖然兩種模式(放在同一個元件或分開不同元件)所需的程式碼(成本)是一樣的,但它至少省下多組件管理的部分工作,等於省去版號管理和發布管理方面的工作 - 這其中的工作量其實可不小。
這其實是FitNesse的早期策略,FitNesse的Web伺服器相關的元件被設計成可與Wiki分離,但我們不希望使用者必須下載兩個元件才能使用,因此我們決定讓使用者只下載一個jar檔案就可以執行它,這樣使用者不必再去找其他的Jar檔案,也不必考慮版本的相容性等等。
但隨著時間推移,將Web元件獨立的需求越來越少,Web元件與Wiki元件的隔離也弱化了,到如今,我們若真的要分離Web組件的話,會需要不少工作量。
我的理解是,即使當初的設計是分離的,但放在一起久了也很容易日漸耦合,我想這就是省下成本的代價吧。
單向的邊界
在設計一套完整的系統架構邊界時,往往需要用反向介面來維護邊界兩側元件的隔離性,這種雙向的隔離性通常不會是一次性工作,它需要持續地長期投入資源維護下去。
下圖中,你會看到一個臨時佔位的,將來可以被替換成完整邊界夠簡單的結構,這個結構採用了傳統的策略模式(Strategy pattern),如你所見,client使用的是一個由service Impl實現Service Boundary介面。
很明顯,上圖的設計已經為未來建構完整的架構邊界達下良好的基礎,必要的依賴反轉已經做完了。但我們從虛線箭頭也可發現未來可能很快會出現的隔離問題,由於沒有採用雙向反向介面,這部分只能依賴開發者和架構師的自律性來保證元件的永久隔離。
策略模式 wiki
策略模式作爲一種軟體設計模式,指對象有某個行爲,但是在不同的場景中,該行爲有不同的實現算法。比如每個人都要「交個人所得稅」,但是「在美國交個人所得稅」和「在中華民國交個人所得稅」就有不同的算稅方法。策略模式:
- 定義了一族算法(業務規則);
- 封裝了每個算法;
- 這族的算法可互換代替(interchangeable)。
註記:
- 实线箭头:用于表示强依赖性、结构性的关系,如继承、实现、组合和聚合。
- 虚线箭头:用于表示弱依赖性、行为性的关系,如依赖和实现。
FACADE模式
採用FACADE模式是一個更簡單的架構邊界設計,如下圖所示,在這種模式下我們連依賴反轉的工作都省了(不必每個service類別都建立一個介面),我們將邊界的定義交給一個Facade類別,如此,client就不會直接直接對到service。
請注意,Client會傳遞性依賴所有Service類,在靜態語言中,如果我們改了Service類別裡面的程式碼都會需要將Client做重新編譯。另外在這個架構中,我們要建立一個反向介面是很容易的。
FACADE範例
總結
本章中介紹了三種實現不完全邊界的三種方式,當然,類似的實現方式還有很多種,本章只是列出常見的三種方式,每種方式都有對應的優點(獲益)與缺點(成本),它們都是被用來充當最終完整架構邊界的一種臨時替代品,架構師的職責之一就是預判預來哪裡有可能會需要設計邊界,並決定該以完全或是不完全形式來實現它。