演進式架構

一直以來,由於軟件架構涉及範圍廣且內涵不斷變化,開發人員不斷嘗試給它一個簡潔的定義。Ralph Johnson 就將其定義爲“重要的東西(無論那是什麼)”。架構師的工作就是理解和權衡那些“重要的東西”(無論它們是什麼)。

爲了給出解決方案,架構師工作的第一步是理解業務需求,也即領域需求。這些需求是使用軟件來解決問題的動機,但終究只是架構師在構建架構時需要考慮的因素之一。架構師還必須考慮其他很多因素,其中一些比較明確(比如清楚地寫在性能服務水平協議裏),還有一些則隱含在商業活動中不言自明(比如公司正着手併購重組,軟件架構顯然也要有變動)。所以對於軟件架構師來說,架構水平體現了他們在權衡業務需求和其他重要因素後找到最佳方案的能力。軟件架構涵蓋了所有這些架構因素,如圖 1-1 所示。

圖 1-1:架構的完整概念,包括需求及“各種特徵”

如圖 1-1 所示,業務需求與其他架構關注點(由架構師定義)並存。這包括各種外部因素,這些因素可以改變構建軟件系統的決策過程。表 1-1 列舉了一些例子。

表1-1:部分特徵列表

可訪問性可問責性準確性適應性可管理性
可負擔性敏捷性可審計性自治性可用性
兼容性可組合性可配置性正確性可信度
可定製性可調試性可降級性可確定性可演示性
可信賴性可部署性可發現性分佈性耐久性
有效性高效性可用性可擴展性故障透明性
容錯性保真性靈活性可檢查性可安裝性
完整性互通性可學習性可維護性可管理性
移動性可修改性模塊性可操作性正交性
可移植性精確性可預測性過程能力可生產性
可證性可恢復性相關性可靠性可重複性
重現性彈性響應性可複用性穩健性
安全伸縮性無縫性自我維持性可服務性
安全性簡單性穩定性標準合規性可生存性
可持續性可裁剪性可測試性及時性可追溯性

在構建軟件時,架構師必須明確哪些特徵最重要。然而,許多因素是互相矛盾的。比如,讓軟件具備高性能的同時還要實現極大的伸縮性就很困難,因爲實現這兩者需要謹慎地平衡架構、運維及其他諸多因素。因此,在爲架構設計做必要分析的同時,又要處理好各個因素之間不可避免的衝突,架構師在權衡每個架構設計方案的利弊時,常常需要做出非常艱難的折中。近年來,軟件開發核心工程實踐的持續發展給我們提供了條件,使我們得以重新思考架構隨時間的推移要如何變化,以及當這樣的演進發生時,如何保護重要的架構特徵。本書將這些部分聯繫起來,以一種新的方式思考架構時間

我們想爲軟件架構添加一個新的標準“特徵”——演進能力

1.1 演進式架構

無論我們怎麼努力,軟件依然變得越來越難以改變。由於各種原因,軟件的組成部分不容易變更,而且隨着時間推移變得愈發脆弱和難以操作。軟件項目的變更通常是由於對功能或範圍做了重新評估而導致的,但是還有一些變化是架構師和長期規劃者無法控制的。儘管架構師喜歡爲未來做戰略性規劃,但不斷變化的軟件開發環境使這一切變得困難重重。既然變化是必然的,那麼我們就只能因勢利導地來利用它。

1.1.1 一切都在變化,如何才能長期規劃

生物世界中,環境因自然因素和人爲因素而不斷變化。例如,20 世紀 30 年代初,澳大利亞的甘蔗受到甲蟲危害,導致甘蔗作物嚴重減產,利潤大減。1935 年 6 月,作爲應對措施,當時的甘蔗實驗站管理總局引入了甘蔗蟾蜍來捕食甲蟲,這種蟾蜍原本只產於中美洲和南美洲。短暫餵養後,1935 年 7 月和 8 月在昆士蘭州北部投放了甘蔗蟾蜍。因爲它們的皮膚有劇毒,並且在當地沒有天敵,很快這種蟾蜍就氾濫成災了。如今,澳大利亞的甘蔗蟾蜍大約有 2 億隻。這件事告訴我們:向高度動態的(生態)系統中引入變化,可能會產生無法預料的結果。

軟件開發體系由所有的工具、框架、庫以及最佳實踐(軟件開發領域的技術積累)構成。和生態系統一樣,軟件開發體系實現了平衡,開發人員能夠理解這個體系併爲其添磚加瓦。然而,這種平衡是動態的,隨着新事物不斷出現,平衡不斷被打破和重建。想象一個腳踏獨輪車,手裏還拿着盒子的人。他是動態的,因爲他需要不斷調整來保持挺立;他又是平衡的,因爲他保持着身體平衡。在軟件開發體系中,每一項創新或新實踐都可能打破現狀,迫使系統重新建立平衡。就好比我們不斷地將更多的盒子拋向騎獨輪車的人,迫使他不斷尋求新的平衡。

架構師在很多方面都和這個倒黴的獨輪車手相似,不斷地平衡以適應環境變化。持續交付這項工程實踐使得這個平衡過程有了結構性的轉變,它將過去孤立的功能(例如運維)合併到了軟件開發的生命週期中,這讓我們對變化的含義有了新的認識。企業級架構師不能再依賴靜態的五年計劃了,因爲整個軟件開發體系在不斷變化,任何一個長期計劃都可能變得毫無意義。

即便對經驗豐富的實踐者來說,顛覆性的創新也是難以預測的。比如,像 Docker 這樣的容器化技術的崛起就是一個不可預知的行業轉變。但我們仍然可以通過一系列小的演進找到一些蛛絲馬跡。以前,操作系統、應用服務器和其他基礎設施都是商品,需要斥巨資購買使用許可。當時許多架構設計着重於高效利用共享資源。漸漸地,Linux 變得足以支撐企業及應用,使得購買操作系統的費用降爲零。接下來,通過 Puppet 和 Chef 等工具自動配置服務器的 DevOps 實踐使得 Linux 運維工作也不再需要成本。一旦開發環境免費並得到廣泛應用,勢必朝着更加通用和便攜的方向發展,於是 Docker 應運而生。但如果沒有之前所有的演進過程,容器化就不會發生。

我們所使用的編程平臺也在持續演進。新的編程語言提供了更好的應用編程接口(API),提高了對新問題的靈活性和適用性。新的編程語言還提供了不同的範式和概念。例如,引入 Java 替代 C++,降低了編寫網絡代碼的難度並改善了內存管理。回顧過去的 20 年,很多語言一直在持續改進它們的 API,與此同時,新的編程語言往往用於解決新的問題。編程語言的演變如圖 1-2 所示。

圖 1-2:流行編程語言的演進過程

無論是在軟件開發的哪個方面,比如編程平臺、編程語言、運維環境、持久化技術等,我們都知道改變會持續發生。雖然無法預測技術或領域格局何時會改變,或哪些變化會持續下去,但我們清楚改變是不可避免的。因此,我們應該在構建系統的過程中對這一點保持清醒的認識。

如果整個體系持續地以出乎意料的方式發生變化,預測變化就變得不可能了,那麼用什麼來替代固定計劃呢?企業架構師和開發人員必須學會適應變化。做長期計劃有一個隱含的原因是財務上的考慮,因爲以前軟件變更的成本很高。但是,現代工程實踐通過自動化和其他先進實踐(例如 DevOps)降低了軟件變更的成本。

多年來,一些聰明的開發人員發現系統的某些部分相對而言更難修改。這便是將軟件架構定義爲“將來難以變更的部分”的原因,這個省事的定義簡單地區分了軟件系統中真的難以修改的部分和可以輕鬆修改的部分。但這個定義依然不免走進了盲區,因爲開發者預先假設“變更是困難的”,這變成了一個自證式的預言。

幾年前,一些富有創新精神的架構師用新的視角審視了“將來難以變更”的問題。使架構具有可變性會怎樣呢?換句話說,如果易於改變是架構的基本原則,那麼變更將不再困難。反過來,使架構具備演進能力會導致一組全新的行爲出現,進而再次打破整個體系的平衡。

即使環境不改變,架構特徵出現磨損該怎麼辦?架構師設計出架構,將其置於紛亂的現實世界,基於架構執行各項事務。架構師要如何保護他們定義的重要部分呢?

1.1.2 完成架構構建後,如何防止它逐漸退化

有一種不幸的退化叫作架構比特衰減,它在很多組織中均有發生。架構師選擇特定的架構模式來滿足業務需求及讓系統具備某些能力,但這些特徵常常意外地隨着時間推移而退化。例如,架構師構建了一個包含頂部展現層、底部持久層和一些中間層的分層架構。負責報表功能的開發人員出於性能上的考慮經常會要求繞過中間層,直接從展現層訪問持久層。架構師通過分層來隔離變化。而後開發人員繞過這些層,不僅增加了耦合,還使得分層變得毫無價值。

定義了那些重要的架構特徵後,架構師如何保護這些特徵不磨損呢?答案是添加演進能力作爲新的架構特徵,使其在系統演進時保護其他特徵。比如,架構師追求架構設計的高伸縮性,但不希望在系統演進時削弱該特徵。因此,演進能力是一種元特徵和保護其他所有架構特徵的架構封裝器。

本書將闡明演進式架構的另一個作用,即它是一種爲架構的重要特徵提供保護的機制。我們探尋持續架構背後的理念。持續架構指構建架構的過程沒有最終狀態,它會隨着軟件開發體系的不斷變化而演進,並保護重要的架構特徵。我們不會嘗試定義整個軟件架構,因爲已經存在很多定義了。我們通過引入時間變化作爲頭等架構元素來擴展當前的定義。

我們對演進式架構的定義如下。

演進式架構支持跨多個維度的引導性增量變更。

1.2 增量變更

增量變更描述了軟件架構的兩個方面:如何增量地構建軟件和如何部署軟件。

在開發階段,允許小的增量變更的架構更易於演進,因爲對於開發者來說,變更範圍相對更小。對部署而言,增量變更指業務功能的模塊化和解耦水平,以及它們是如何映射到架構中去的。示例如下。

假設有一個小工具商家 PenultimateWidgets,該商家採用微服務架構和一些現代工程實踐運營着一個產品目錄頁面。該頁面的一個功能是讓用戶給出小工具的星級評分。PenultimateWidgets 的其他一些業務也需要評分,比如客服代表、物流服務商評價等,所以這些業務共享星級評分服務。某天,該服務的開發團隊發佈了新版本,新版本允許用戶給出半星評價(一個小而重要的升級)。其他需要評分的服務不需要強制升級,而可以逐漸地在合適的時候遷移到新版本。PenultimateWidgets 的 DevOps 實踐採用了架構級別的監控,不僅監控各個服務,還監控服務與服務之間的路由。當運營人員觀察到在給定時間內沒有請求路由至特定服務時,他們自動將該服務從體系中移除。

這是一個在架構級別進行增量變更的例子:如有需要,原服務和新服務可以並存。團隊可以在適當(不太忙或必要)的時候完成遷移,當所有遷移都完成時,舊版本的服務將會作爲垃圾被自動回收。

增量變更的成功需要一些持續交付實踐的配合。並不是任何情況都需要所有這些實踐,但通常它們會一起發生。第 3 章將討論實現增量變更的方法。

1.3 引導性變更

一旦架構師選擇了重要的架構特徵,他們會把變更引導進入架構,以保護這些重要特徵。爲此,我們借用演化計算中的一個概念:適應度函數。該函數是一種目標函數,用於計算潛在的解決方案與既定目標的差距。在演化計算中,適應度函數決定一個算法是否在持續提升。換句話說,隨着每個算法變體的產生,基於設計者對算法“適應度”的定義,適應度函數決定每個變體的“適應程度”。

對於演進式架構,隨着架構的演進,我們有着類似的需求。我們需要評估機制,來評估變化對架構重要特徵的影響,並防止這些特徵隨着時間的推移而退化。適應度函數的隱喻涵蓋多種機制,包括度量、測試和其他檢驗工具。我們採用這些機制來確保架構不會以不良方式變更。當架構師確定了需要保護的架構特徵時,他們會定義一個或多個適應度函數來提供保護。

以往,架構往往要劃出一部分作爲管理活動,最近架構師才接受了通過架構實現變更的思想。架構適應度函數允許在組織需求和業務功能的上下文中制定決策,併爲明晰且可測試的決策奠定了基礎。演進式架構並不是毫無約束或不負責任的軟件開發方式。相反,它可以在高速變遷的業務、嚴謹的系統需求和架構特徵間找到平衡。適應度函數驅動架構設計決策,並引導架構變更適應業務和技術環境的變化。

第 2 章將詳細介紹使用適應度函數指導架構演進。

1.4 多個架構維度

不存在單獨的系統。世界是一個整體。如何劃分系統邊界取決於討論的主題。

——Donella H. Meadows

古希臘的物理學通過固定點分析宇宙,最終發展成了經典力學。但是到了 20 世紀初,隨着儀器越來越精密,現象越來越複雜,人們逐漸從經典力學轉向相對論。科學家意識到之前被視爲孤立的現象其實是相互影響的。自 20 世紀 90 年代起,受到啓發的架構師越來越多地將軟件架構視作多維的。持續交付也將運維納入了軟件架構的範疇。軟件架構師往往關注技術架構,但那只是軟件項目的維度之一。如果架構師想構建可演進的架構,就必須考慮系統中所有會受變化影響的部分。正如物理學所講的,萬物都是相關聯的,架構師深知軟件項目是多維的。

爲了構建可以不斷演進的軟件系統,架構師不能只考慮技術架構。例如,如果項目包含一個關係型數據庫,那麼數據庫實體之間的結構和關係也會隨着時間的推移而不斷變化。另外,架構師也不希望系統在演進過程中暴露安全漏洞。這些例子展示了架構的不同維度,它們通常在架構中以正交方式交織在一起。部分維度在常見的架構關注點範圍之內(見表 1-1),但是架構維度的意義其實更廣泛,它囊括了很多傳統意義上技術架構範疇之外的東西。每個項目都有許多維度,架構師在考慮架構演進時必須要想到。下面是一些影響現代軟件架構演進能力的常見維度。

  • 技術

    架構中的實現部分:框架、依賴的庫和實現語言。

  • 數據

    數據庫模式、表格佈局、優化計劃等。通常由數據庫管理員(DBA)處理這類架構。

  • 安全

    定義安全策略、指導方針和指定工具來幫助發現缺陷。

  • 運維與系統

    關注架構如何映射到現有的物理或虛擬的技術設施中,包括服務器、機器集羣、交換機、雲等。

以上每個視角構成一個架構維度——爲了支持特定視角而有意進行的劃分。這裏架構維度的概念涵蓋了傳統的架構特徵和其他有助於構建軟件系統的因素。隨着問題和周遭環境的改變,我們想保護架構不磨損,每個維度都提供給我們一個審視架構的視角。

從概念上劃分架構的方法有很多,比如 IEEE 的軟件架構定義中的 4+1 視圖模型。它關注不同角色的不同視角,將整個系統劃分成了邏輯視圖開發視圖進程視圖物理視圖。在著名的《軟件系統架構》一書中,作者給出了軟件架構的視點目錄,它涵蓋了更多角色。類似地,Simon Brown 的 C4 建模符號也從概念上幫助組織區分關注點。不過,本書沒有嘗試創建任何維度分類,而是識別那些現有項目中的維度。從實用角度來看,不論如何對關注點進行分類,架構師都需要保證這些維度不磨損。不同的項目有不同的關注點,這導致每個項目都有特定的維度。對於新項目,以上任何技術都能提供有用的見解,但是對於現有的項目,我們必須處理眼前的實際情況。

按照架構的維度思考,通過評估重要維度對變化的響應,架構師可以分析不同架構的演進能力。隨着系統與互相沖突的問題(伸縮性、安全性、分佈式、事務性等)關聯得越來越緊密,架構師必須跟蹤更多的維度。只有結合所有這些重要維度,思考系統將如何演進,才能構建出可以不斷演進的系統。

項目的整個架構範圍由軟件需求和其他維度構成。當架構和整個體系隨着時間的推移一起演進時,我們可以使用適應度函數來保護架構特徵,如圖 1-3 所示。

圖 1-3:架構由需求和其他維度構成,每個維度都受適應度函數保護

在圖 1-3 中,架構師確定了可審計性數據安全性性能合法性伸縮性是該應用的關鍵架構特徵。隨着業務需求不斷變化,每個架構特徵都通過適應度函數來保護其完整性。

我們強調架構整體的重要性,但也應意識到,技術架構模式及相關議題也是架構演進的很大一部分,比如耦合和內聚。第 4 章將討論技術架構耦合對演進能力的影響,第 5 章將討論數據耦合的影響。

耦合不只和軟件項目的結構元素有關。近來,很多軟件公司認識到了團隊結構對架構的影響。我們將討論軟件中的各類耦合因素,但是團隊結構的影響出現得如此早且頻繁,我們需要馬上就此展開討論。

1.5 康威定律

1968 年 4 月,梅爾文 • 康威在《哈佛商業評論》上發表了一篇名爲“How Do Committees Invent?”的論文。在這篇論文中,康威提出:社會結構,特別是人與人之間的溝通途徑,將不可避免地影響最終的產品設計。

康威描述道,在設計的最初階段,人們首先需要高瞻遠矚地思考如何將職責劃分爲不同的模式。團隊分解問題的方式會左右他們之後的選擇,這便是康威定律

在設計系統時,組織所交付的方案結構將不可避免地與其溝通結構一致。**

——梅爾文 • 康威

正如康威所描述的,當技術人員將問題分解成更小的塊,使其更易於委派時,就會產生協調問題。很多組織爲了解決協調問題,會設置正式的溝通結構或是建立森嚴的等級制度,但這樣的解決方案往往是僵化的。比如,按照技術職能(用戶界面、業務邏輯等)劃分的團隊,他們在處理常見的跨職能問題時協調的開銷就會增加。那些從創業團隊跳槽到跨國公司工作的人,很可能會經歷兩種截然不同的文化:前者非常靈活且適應性很強,而後者的溝通結構往往是僵化的。康威定律一個很好的例子就是修改兩個服務間的契約。如果一方想修改服務,而這需要另外一方的同意和配合,那麼這件事就可能變得很難。

在論文中康威特別提醒軟件架構師,不要只關注軟件架構和設計,還應關注團隊之間委派、分配和協調工作的方式。

在很多組織中,團隊是根據職能來劃分的。示例如下。

  • 前端開發團隊

    負責用戶界面(UI)技術(如 HTML、移動端和桌面端)。

  • 後端開發團隊

    專門構建後端服務(有時還包括 API 層)。

  • 數據庫開發團隊

    專門構建存儲和邏輯服務。

在這樣的組織中,管理層從人力資源的角度簡單地按照職能劃分團隊,沒有充分考慮工程效率。雖然每個團隊都有其擅長的領域(比如構建一個視圖,增加一個後端 API 或服務,或者開發一個新的存儲機制),但是當需要發佈新的業務功能或特性時,三個團隊都要參與其中。各個團隊通常都會針對眼前的任務優化效率,而不是針對那些更抽象的戰略業務目標(特別是有工期壓力時)。這會導致各團隊往往專注於交付各自的組件,而不關注端到端的特性價值,導致這些組件可能無法高效協作。

在這樣的團隊編制下,由於每個團隊都在不同的時間忙於自己的組件,因此那些依賴所有團隊的特性需要花費更長的時間。例如,修改目錄頁這樣常見的業務變更涉及 UI、業務規則和數據庫模式的變更。如果每個團隊都各自爲戰,那麼他們必須協調時間表,這將增加實現該特性所需的時間。這個例子很好地解釋了團隊結構對架構和演進能力的影響。

康威在論文裏提到:“每當新的團隊組建,其他團隊的職責範圍會縮小,能夠有效執行的可選設計方案也會隨之變少。”換句話說,人們很難改變其職責範圍外的事情。軟件架構師需要時刻關注團隊的分工模式,從而使架構目標和團隊結構保持一致。

很多構建架構(如微服務)的公司圍繞服務邊界構建團隊,而不是按孤立的技術架構來劃分。在 ThoughtWorks 技術雷達中,我們稱之爲“康威逆定律”。以這種方式組織團隊是理想的,因爲團隊結構會影響軟件開發的很多維度,並且會反映問題的大小和範圍。比如,在構建微服務架構時,企業通常會構建與架構相仿的團隊結構,通過打破功能筒倉使每個團隊都有人能考慮到業務的各個角度和技術的方方面面。

 構建與目標系統架構相仿的團隊結構,這樣項目會更容易實現。

 

PenultimateWidgets 及其逆康威時刻

本書以 PenultimateWidgets 公司爲例。它是排行倒數第二的小工具經銷商,還是一家大型在線小工具(各種小商品)商家。這家公司正在更新其大部分 IT 基礎設施,包括一些想暫時保留一段時間的遺留系統和正在迭代開發中的新戰略系統。本書將重點介紹 PenultimateWidgets 所遇到的問題以及採取的解決方案。

他們的架構師首先觀察了軟件開發團隊。舊的單體應用採用了分層架構,將展現、業務邏輯、持久化和運維分離開來。而他們的團隊設置也和架構如出一轍:所有前端開發人員在一起工作,後端開發人員和數據庫管理員也各自工作,運維則外包給了第三方。

當開發人員開始在架構(基於具有細粒度服務的微服務架構)上工作時,協調成本急劇增加。因爲服務是圍繞領域(例如用戶結算)而不是技術架構來構建的,所以在變更單個領域時將需要各團隊間進行大量的協調。

於是,PenultimateWidgets 採取了康威逆定律,建立了和服務範圍相匹配的跨職能團隊:每個服務團隊包括服務負責人、幾位開發人員、一位業務分析師、一位DBA、一位質量保障人員和一位運維人員。

本書多處提到了團隊的影響,並會通過示例說明它帶來的結果。

1.6 爲何演進

對於演進式架構,一個常見的問題是關於其名稱的:爲什麼叫作演進式架構而不是別的?比如增量式持續式敏捷式響應式應急式,等等。這些術語都不夠準確達意。這裏所說的演進式架構包含了兩個關鍵特徵:增量和引導。

持續式敏捷式應急式都表達了隨時間不斷變化的概念,這確實是演進式架構的關鍵特徵,但是這些術語都沒能準確地表達出架構將如何變化,或者說期望的架構最終是什麼樣子的。雖然這些術語都隱含着環境變化,但是都沒能涵蓋架構應有的樣子。而在演進式架構的定義中,引導的含義反映了我們想實現的架構,即我們的最終目標。

相比可適應這個詞,我們傾向於演進式,因爲我們對經歷根本性演變的架構感興趣,而不是那些經歷了修修補補後變得越來越難以理解、複雜問題頻發的架構。適應意味着解決方案不求優雅或長久,只要能找到方法使系統運作就行。爲了構建能實實在在演進的架構,架構師必須支持真正的變化,而不是權宜之計的考慮。回到我們的生物學隱喻,演進是這樣一個過程:建立一個適用的並能在其所處的不斷變化的環境中持續運行的系統。系統可能存在個別的適應性,但是架構師應該關心整體可演進的系統。

1.7 小結

演進式架構主要由三方面構成:增量變化、適應度函數和適當的耦合。本書餘下的部分將分別討論它們,然後再將它們結合起來,幫助我們構建和維護支持不斷變化的架構。

本文摘自《演進式架構》

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章