如何擺脫令人頭疼的架構依賴?
我猜你一定了解以下這種感覺:你馬上要準備在某個項目審查會議中發言,而你已知道自己陷入了麻煩中。看看這個僅有5頁的幻燈片,你深切地感覺到這個會議注定不會進行得很順利。即使經過連續三個晚上的通宵奮戰,將原本16頁的演講精簡為4頁的模板,你也無法感覺安心。對于意料之外的技術依賴性所做的任何不穩定性說明都會像一個紙牌屋一樣轟然倒塌。而你將因此得到一系列對你的職業生涯有阻礙的資料。接下來的30分鐘內,項目總監和她的管理團隊將進一步聲明:架構師沒有完成任何實際的工作。
哪里出了錯?
架構師理應了解整個系統與其中的各個元素是怎樣聯結在一起的,因此這是一個非常重要的角色。但是團隊往往無法理解架構依賴中真正的復雜性,等到真正理解的時候,往往已經太遲了。
我有一個同事曾經表示,當代碼被共享后,依賴就生產了。“代碼共享”的例子包括API、服務規格,以及靠“拷貝與粘貼”方式進行編程。但以上這些并不是依賴產生的所有來源。
架構性依賴的產生是在我們對架構信息——更通用地來說是架構知識進行“共享”時產生的。而在我們創建集成系統時,分享架構知識是不可避免的。這種依賴往往具有很高的復雜性,而且更加難以察覺。
麻煩的地方在于,按照架構的定義,它本身代表了架構元素(例如組件、進程或實體等等)之間的關系。有人可能會爭辯說:架構規格本身就是架構級依賴的完整集合了。但是,如果架構的共享是隱式的,即是說我們做出了某些假定,那么危險就會隨之產生。而在大規模的企業級環境中,不進行任何假定幾乎是不可能的。
難以捉摸的依賴
幾年之前,我曾經采訪過某個項目團隊,他們當時正打算替換一個陳舊的企業級應用程序。該應用程序中的一項核心功能是開發票,經過多年的成長,該公司在這一方面的業務得到了擴展。這種擴展是由外部應用向核心產品應用程序推進的,原因有很多,包括對第三方的支持,以及可維護性等等。
在替換過程中,對于該擴展如何處理舊系統中的貨幣的四舍五入的策略(例如,如何處理每個計算步驟中的小數),在設計方面的假設正變得逐漸具體化。該擴展的設計者在界面規格的設計上非常小心謹慎,新的應用程序在功能上和技術上都做到了與該擴展的兼容……問題在于,客戶發票看起來存在著一些根本的四舍五入錯誤。
在這里我們學到了很明顯的一堂課,即避免對其它系統的工作方式進行任何假設,通過防御式的設計,可以將外部依賴性盡可能降至最低。但不一定總是能做到完全消除以下問題:
- 語義與生命周期:所有的架構元素都會假設某種形式的語義,以及某種隱含的生命周期。這兩者很難用界面規格及架構模型進行表達,但卻是非常重要的。
- 對相同邏輯元素的多種實現:一旦你的公司決定采購某些現成的打包應用,這種情況就成為不可避免的現實了。
- 手動整合(例如需要通過人工方式將某些數據從某個系統中遷移至另一個系統):這種整合活動在架構模型上往往是不可見的。如果項目團隊不打算投入力量開發一個復雜的界面,以實現對相對少量數據的自動轉換,那么這種整合活動就會出現。但是人工操作并不能消除依賴。
并不是說封裝的概念在這里不起作用了,事實上它依然在發揮作用。但如果我們不能理解橫跨多個架構關注面的依賴的影響,也不清楚如何緩解和管理這些依賴,那么問題就會出現。
在上面示例中,有關貨幣四舍五入的策略就表明架構知識在兩個不同的應用中進行了共享。舊應用程序與新擴展中的架構元素各自實現了不同的角色和功能,而“擴展”功能的架構師在這里對舊應用程序中的四舍五入策略進行了某種假設。
換句話說,架構級依賴就是指某個系統(或多個系統)中,在兩個或多個元素間產生了架構知識的共享,以允許其中至少一個元素能夠被設計、重組、替換或進行操作。
我們可以將這種共享的(在某些情況下是假設的)知識建模為一種抽象的元素,包含兩種獨立的實現,并將其稱之為“共享的元素”。請注意,這種共享的元素與接口的意義是不同的,雖然后者同樣也不需要其它應用的某個精準的模型。
為什么“共享元素”難以發現?
在使用傳統的依賴分析手段時,我們的技術環境的復雜性會產生至少三種問題:
知識的問題
優秀的架構師會以他所知的最佳知識來定義一個架構,他將這個架構提交給合作者,讓專家、業務代表和其他架構師在工作間中進行審查,但在尋找依賴的時候,他們卻會遇到三種麻煩:
- 由于架構師對于其它系統的知識有所局限,因此審查者的存在是必要的,但審查者們對于新的系統的了解同樣也是有限的。為了讓參與者獲得足夠的共識以發現潛在的依賴,需要進行艱苦的斗爭。
- 項目的范圍過大,使得架構師很難保持一個單獨的視角,這也增加了解決方案產生碎片化的風險 —— 參見觀點1。
- 架構師無法描述整個系統的系統,這導致他們只能關注于“已知范圍內”的解決方案(這是很自然的)。但眾所周知,項目范圍定義在依賴的說明方面做的一向很糟糕,依賴不僅僅停留在項目所定義的范圍內 —— 請再次參考觀點1。
所有這些因素都會影響到審查的過程,最終的結果是架構師們無法正確地提問,至于有關的依賴和相關聯的影響是否真正確定下來,一定程度上得看運氣如何了。
數據的問題
依賴分析工具通常依賴于源代碼或是詳細的模型(UML、ArchiMate或類似工具)。在一個異質的技術環境中,要想得到一個幾乎完全完整的模型幾乎是不可能的。你們有哪位曾經得到過某個整合系統的精確模型?請舉手看看。不,我想這種情況是不可能存在的。這并不是說這些工具沒有價值,只是說它們在分析主機系統、ERP和新型網站之間的依賴時起不到什么作用罷了。此外,這些工具傾向于僅對源代碼結構進行關注,而對所有不可捉摸的依賴視而不見。
建模的問題
如果依賴在界面規格中是清晰可見的,那么我們還有機會對它進行正確的處理。但如果這些依賴不可見,那情況又會變成怎樣呢?諸如UML和ArchiMate這樣的建模語言能夠讓使用者通過兩個模型元素之前抽象的“關聯”類型或“依賴”鏈接,捕獲到依賴(這些依賴不僅僅是常見的元素至元素之間的關聯)的存在。這種功能確實很有用,它讓我們能夠明確地將依賴進行文檔化,不過……那些沒有記錄下來的依賴就將被人遺忘。
但是,雖然這些功能確實能夠建模出兩個架構上的元素之間的依賴,但它們并不能夠幫助我們詳細地了解該元素的依賴具體是在哪一部分。如果我們只是在模型中簡單地說,元素“X”依賴于元素“Y”,這不能夠表明“X”具體依賴于“Y”中的哪一部分,我們只能假設“X”依賴于“Y”的全部。這就導致了依賴的“放大”,因為“X”或許僅僅依賴于“Y”中的某一個元素(比如說“Y.A”,但這個元素在界面上或許是不可見的。接下來的問題就在于“Y.A”中對應的屬性了,換句話說,“Y.A”中的哪些部分是需要對依賴進行建模的呢?
暗示:“Y.A”即是“共享的元素”。
我們又該怎樣應對這一問題呢?
最大的挑戰在于如何避免“好高騖遠”,在整個技術環境中地毯式地搜索各種依賴的存在。我們可以將最初的問題 “我們的新系統會依賴于哪些外部元素?”倒過來問:“在我們的新系統中的哪些元素不是新系統本身能夠負責處理的?”
你看到發生什么事了嗎?
大洋變成了一個小水坑,更重要的是,你對這個小水坑能夠加以控制了。這樣一來,就使得依賴的搜索范圍大大地減少了,并且相關的知識和數據方面的問題更變得更加容易管理了。
但是,我們為什么要追求對元素的控制呢?
原因在于,如果你的新系統中的某個元素使用了某些數據,或是實現了某種算法,并且因此使得你的系統無法成為企業引用(或稱之為“控制拷貝”)的話,那么就說明該元素依賴于某個系統中的其它元素。其中一種情況就是“共享元素”的實現。我們不需要知道那個“其它元素”的具體位置,但已經知道需要管理某個依賴了。這一方法也適用于數據的問題,即我們只需了解我們自己的系統就足夠了。
我們依然需要對依賴進行建模,這樣才能夠與相關的干系人交流該依賴的問題。在理想的情況下,我們可以將共享的元素在自己的架構中進行建模,因為共享元素可以成為系統中的某個抽象元素,而不是實際的設計元素。
我們也不希望被迫對其它系統進行架構建模,因為它不僅增加了我們的工作量,并且也不是我們的交付范圍中的一部分。但我們需要表達出我們如何管理由于共享某個架構元素所造成的影響的方法。因此,總的來說,我們需要一種依賴模型,以表達出某個架構依賴的三種組成部分:
- 依賴關系,
- 共享元素,
- 以及依賴管理所必須的策略,例如:我們如何協調共享元素所包含的信息。
為了避免依賴成為嚴重的問題,第三個部分是相當重要的。在以上那個對貨幣進行四舍五入的示例中,由于依賴的存在,因此企業應用和它的擴展之間必須進行設計決策的同步。另一種方案是,我們可以通過加入新的功能,讓它協調最終結果,而隱藏某些中間的計算過程,以緩解依賴所造成的影響。這兩種方案都是有效的,我們或許也需要對依賴進行管理和緩解。
以下部分描述了一種能夠支持該方式的建模表示法。
依賴驅動建模
第一個任務是將抽象的“共享元素”關系轉化為一種我們可以建模的實體。為了更清晰地說明這一點,讓我們考慮一下下面這個示例:某個“Person”實體與“Company”實體是相互分離的,但如果這個人受雇于這家公司,那么我們就能夠創建一個關系,其中包括了自有的屬性。我們可以將這個實體建模為“Staff”。
“Staff”現在能夠表達兩種含義:1) “Person”和“Company”之間的一種關系,并且這種關系與Person和Company本身的建模是無關的,2) 一系列特定于該關系的屬性,例如employment id, salary,office location,role等等。這些屬性對于理解必須的依賴管理是非常重要的。比方如,如果該員工不了解工資、工作地點和職位,他是不太可能繼續在這家公司打工的。
“Staff“表現了依賴中共享元素的角色
依賴關系
根據Callos等人在這篇論文中的內容所說,基本的架構依賴類型共分兩種:即結構型依賴和行為型依賴。結構型依賴包括了軟件包、服務或其它類型的API之間的鏈接,而行為型依賴包括了界面驗證邏輯,或是以上的示例中所說的貨幣四舍五入策略。我們可以將這兩個基本的依賴類型與共享元素相結合,正如下圖中所描繪的那樣。
共享元素通常來說是數據和處理元素的聚合體,但在最抽象的形式中,共享元素也能夠涵蓋對架構模式、策略或其它依賴的引用。而對其它依賴的引用使得傳遞性依賴的建模成為可能。
元素的邊界展示了每個架構視圖或風格的選擇范圍。元素視圖能夠幫助我們確定必須進行管理的部分(例如必需的共享元素協調)。舉例來說,一個功能性視圖需要對功能性設計信息進行協調,而一個組件-連接器視圖則需要對通信配置(例如web service資源)進行管理。如果想更多地了解有關架構視圖和風格的內容,Clements的著作《軟件構架楄檔》是一個很好的起點。
一個共享元素也許是可見的,也許不是。元素“D1”或許能“看到”元素“D2”對于共享元素的定義(“外部的”),或許不能(“內部的”)。我們假設D1處于可控設計范圍之內,而D2則在范圍之外。舉例來說,一個數據查詢API屬于外部的(結構型)依賴,而相關的數據驗證規則表現了內部的(行為型)依賴。正是結構型、行為型依賴類型與外部、內部共享元素的組合,形成了依賴的分類,正如下圖所示。
為了完善整個依賴模型,我們將使用抽象的依賴類型“dependsOn”作為這四種依賴類型的超類。
依賴分類
總的來說,一共有以下五種依賴關系類型的存在(SE表示共享元素):
- “dependsOn”:D1依賴于D2,這種依賴有可能是未知的,也可能是多種類型的依賴。我們將其表示為D1 dependsOn(SE) D2。
- “communicatesWith”:D1在結構上依賴于D2,這種依賴是一種可見的外部元素。我們將其表示為D1 communicatesWith(SE) D2。
- “refersTo”:D1在結構上依賴于D2,這種依賴是一種隱含的內部元素。我們將其表示為D1 refersTo(SE) D2。
- “confluenceWith”:D1在行為上依賴于D2,這種依賴是一種可見的外部元素。我們將其表示為D1 confluenceWith (SE) D2。
- “influencedBy”:D1在行為上依賴于D2,這種依賴是一種隱含的內部元素。我們將其表示為D1 influencedBy (SE) D2。
對依賴進行建模
為了更實際地表現這種依賴模型,我們假設有一個建議的架構,其中的Web應用服務器(D1)將通過企業服務總線(ESB)(如下圖所示)與某個庫存(Inventory)系統進行通信。我們假設該web應用實現了產品目錄和購物車兩種功能,他們都需要使用到“產品(Product)”這個實體。
該公司的企業架構將庫存(D2)系統推舉為所有“Product”實體的控制者。因此web應用就依賴于庫存系統對于“Product”的定義,也就是說,“Product”成為了一種共享元素,在三個系統中各有一種不同的實現。
使用前面所提到的分類方式,我們可以定義出以下的依賴:
Web應用服務器(WAS)dependsOn(1,2,3)庫存系統,其中:
- WAS communicatesWith(Product) ESB
- ESB communicatesWith(Product) Inventory
- WAS refersTo(Product) Inventory
從WAS架構師的角度來說,她至少要對構架組件-連接器視圖進行建模,以表現系統的界面和連接器。該架構師還需要了解WAS的業務需求,基于這些業務需求,她識別出Product實體的以下屬性:
- Product Id和分類(category)
- Product描述(description)
- 產品庫存量(stock level)
架構師將與庫存團隊進行交流,并確認了庫存系統中所管理的數據確實包括了以上幾種所識別出的屬性。她也同時發現,產品描述和分類數據對于新的web應用來說并不適用,但她也無法選擇忽略這些數據,因為這些數據是新產品中所必需的一部分。這就形成了web應用的內容管理流程中的一個關鍵組成部分。
產品Id和庫存量是僅有的兩個通過ESB進行暴露的參數。Web應用需要保持產品Id與庫存系統同步。出于內容管理需求的考慮,架構師選擇依靠內容管理流程確保產品Id的同步性,而不是實現某種搜索功能。
請注意,到目前為止,我們對這三個系統的說明非常少。但我們已經了解了它們之間的依賴性的重要信息,并根據這些信息提煉出一個簡明的系統依賴模型了。
架構師現在就能夠對依賴進行精煉了,結果如下:
- Web應用服務器(WAS)dependsOn(1,2,3)Inventory,其中:
- WAS communicatesWith(Product.ID, Product.StockLevel) ESB
- ESB communicatesWith(Product.ID, Product.StockLevel) Inventory
Web應用服務器refersTo(Product.ID, Product.Category, Product.Description, Product.StockLevel) Inventory
這里的關鍵在于,我們已經將依賴模型轉化為一種我們可以使用傳統的架構設計技術進行不斷地精煉的描述了。并且重要的地方在于,但我們獲取了足夠的細節信息之后,就可以逐個地停止對這些依賴的精煉過程了。
依賴評估
我們的WAS架構師已經識別出架構依賴中的兩個重要部分:即依賴關系和共享元素了。現在,她需要對必需的依賴管理做出決定了。下面的表格概括了已識別出的依賴所造成的架構上的影響。
有趣的是,這張表格對于項目經理來說也是很有價值的,能夠幫助他們識別出項目的任務、必需的資源,以及對每個系統的資源分配。每個依賴都需要團隊的合作,以所提議的協作。
依賴 |
共享元素 |
協作 |
影響 |
WAS communicatesWith ESB |
Product.ID, Product.StockLevel |
- 界面規格協議,以調解WAS與庫存系統之間的互操作。 - 服務配置管理。 - 用ESB調解界面中特定于位置和技術的屬性。 |
如果界面或配置發生了改變,就會產生直接影響。不過,ESB應該隱藏實際的服務URI以及實現技術(實現封裝)。 |
ESB communicatesWith Inventory |
Product.ID, Product.StockLevel |
同上 |
同上 |
WAS refersTo Inventory |
Product.ID |
WAS必須保證產品id與庫存系統的同步。這將由內容管理流程負責。 |
如果id不正確,那么web應用需要為用戶顯示錯誤信息。 |
Product.StockLevel |
對于WAS所發出的某個web service查詢請求,將返回庫存量信息。不需要明確的協作。 |
假設communicatesWith依賴已經得到管理,則影響不存在。 |
|
Product.Category, Product.Description |
產品的分類和描述將通過內容管理流程進行同步。 |
如果內容管理流程出錯,則會導致web應用中出現不正確的產品和描述信息。 |
下方的表格概括了適用于某個依賴的一系列評估參數的一種建議,根據架構、項目和通用上下文的不同,參數也應當隨之變化,但評估的目的依然保持不變:
- 相關的共享元素屬性有哪些?
- 對于每個共享元素屬性,我們應當如何緩和與管理依賴(共享元素協作)?
- 如果協作失敗,會產生什么樣的影響?
場景 |
Identifier |
對架構評估場景的簡單描述 |
依賴 |
Identifier |
某個唯一的依賴Id |
分類類型(Taxonomy Type) |
每個分類一種 |
|
邊界(Boundary) |
元素D1的范圍定義,其中D1始終處于設計范圍之內,而D2則在范圍之外,唯一的例外就是依賴的存在。 The (D1) element(元素) scope(范圍) definition(定義), where the D1 element is within the design scope while D2 is outside except only as a dependency |
|
協作 |
共享元素(Shared Element) |
對于共享元素和模型的描述(如果存在) Description of the shared element and model if available |
特性(Properties) |
相關的共享元素特性列表 The list of relevant(有關的) shared element properties |
|
管理(Management) |
對每個共享元素屬性的管理(共享元素的協調) The management per shared element attribute(屬性) (share element coordination(協調)) |
|
影響 |
協作失敗時的潛在影響 |
如果共享元素的特性未能正確的進行協作,對于被依賴元素所觀察到的影響 The perceived(感知到的) impact to the dependent(依靠的) element, if the shared element properties are not coordinated(調整) correctly |
最后的總結
我希望本文能夠幫助你改進對于依賴的判定、評估和通用變更影響的評定。在很多情況下,項目遇到的麻煩都是因為對于技術依賴的疏忽和誤解所導致的。但直到目前為止,在對高度復雜的技術環境進行依賴分析時,我們幾乎完全依賴于經驗豐富的老手的意見。雖然這些意見很有價值,但它們難以復制、交流,并且隨著系統規模的成長,也變得越來越不可靠了。
依賴驅動建模是一個良好的工具,它能夠讓依賴分析更為系統化、可重復,并且更加可靠。重要的地方不在于我們選擇了哪一種架構觀念,而在于它明確地捕捉到了架構是如何管理依賴的這一點!它的重要性不僅體現在架構的一般質量上,也體現在整個項目計劃的質量上。
而依賴驅動建模最優秀的一點在于,它僅僅建立于你所知的內容。你或許剛剛從評審會議上解脫出來。
參考與延伸閱讀
- Callo Arias, T. B., van der Spek, P., & Avgeriou, P. (2011). 論文:對于依賴分析解決方案的一種由實踐驅動的系統性回顧(A practice-driven systematic review of dependency analysis solutions。 來自《經驗主義的軟件工程》(Empirical Software Engineering)雜志, 16(5), 544-586.
- Clement, P.; Bachman, F.; Bass, L.; Garlan, D.; Ivers, J.;Little, R.; Nord, R. & Stafford, J (2010). 《軟件構架楄檔》. ISDN: 0321552687, Addison-Wesley Professional
- OMG (2009). 《統一建模語言第二版》.
- Parnas, D. L. (1972). 文章:將系統分解為模塊的標準,來自《ACM交流》, 15(12), 1053-1058.
- The Open Group (TOG) (2009). ArchiMate 1.0 規格。
- Tyree, J. & Akerman, A. (2005). 《架構決策:非神秘化的架構》. IEEE Software, 22(2), 19-27.
關于作者
John Brøndum博士是一位企業架構師,擁有15年以上的項目經驗。在2010年,John創建了自己的咨詢公司Brøndum Consulting Pty Ltd,以幫助客戶設計并交付技術解決方案,為客戶的業務創造真正的價值和影響力。John最近在NICTA的支持下,于新南威爾士大學獲得了架構依賴建模的博士學位。如果想了解他的更多信息,請訪問這個網站。