一直以來,因爲軟件架構涉及範圍廣且內涵不斷變化,開發人員不斷嘗試給它一個簡潔的定義。Ralph Johnson 就將其定義爲「重要的東西(不管那是什麼)」。架構師的工做就是理解和權衡那些「重要的東西」(不管它們是什麼)。前端
爲了給出解決方案,架構師工做的第一步是理解業務需求,也即領域需求。這些需求是使用軟件來解決問題的動機,但終究只是架構師在構建架構時須要考慮的因素之一。架構師還必須考慮其餘不少因素,其中一些比較明確(好比清楚地寫在性能服務水平協議裏),還有一些則隱含在商業活動中不言自明(好比公司正着手併購重組,軟件架構顯然也要有變更)。因此對於軟件架構師來講,架構水平體現了他們在權衡業務需求和其餘重要因素後找到最佳方案的能力。軟件架構涵蓋了全部這些架構因素,如圖 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)下降了軟件變動的成本。
多年來,一些聰明的開發人員發現系統的某些部分相對而言更難修改。這即是將軟件架構定義爲「未來難以變動的部分」的緣由,這個省事的定義簡單地區分了軟件系統中真的難以修改的部分和能夠輕鬆修改的部分。但這個定義依然難免走進了盲區,由於開發者預先假設「變動是困難的」,這變成了一個自證式的預言。
幾年前,一些富有創新精神的架構師用新的視角審視了「未來難以變動」的問題。使架構具備可變性會怎樣呢?換句話說,若是易於改變是架構的基本原則,那麼變動將再也不困難。反過來,使架構具有演進能力會致使一組全新的行爲出現,進而再次打破整個體系的平衡。
即便環境不改變,架構特徵出現磨損該怎麼辦?架構師設計出架構,將其置於紛亂的現實世界,基於架構執行各項事務。架構師要如何保護他們定義的重要部分呢?
有一種不幸的退化叫做架構比特衰減,它在不少組織中均有發生。架構師選擇特定的架構模式來知足業務需求及讓系統具有某些能力,但這些特徵經常意外地隨着時間推移而退化。例如,架構師構建了一個包含頂部展示層、底部持久層和一些中間層的分層架構。負責報表功能的開發人員出於性能上的考慮常常會要求繞過中間層,直接從展示層訪問持久層。架構師經過分層來隔離變化。然後開發人員繞過這些層,不只增長了耦合,還使得分層變得毫無價值。
定義了那些重要的架構特徵後,架構師如何保護這些特徵不磨損呢?答案是添加演進能力做爲新的架構特徵,使其在系統演進時保護其餘特徵。好比,架構師追求架構設計的高伸縮性,但不但願在系統演進時削弱該特徵。所以,演進能力是一種元特徵和保護其餘全部架構特徵的架構封裝器。
本書將闡明演進式架構的另外一個做用,即它是一種爲架構的重要特徵提供保護的機制。咱們探尋持續架構背後的理念。持續架構指構建架構的過程沒有最終狀態,它會隨着軟件開發體系的不斷變化而演進,並保護重要的架構特徵。咱們不會嘗試定義整個軟件架構,由於已經存在不少定義了。咱們經過引入時間和變化做爲頭等架構元素來擴展當前的定義。
咱們對演進式架構的定義以下。
演進式架構支持跨多個維度的引導性增量變動。
增量變動描述了軟件架構的兩個方面:如何增量地構建軟件和如何部署軟件。
在開發階段,容許小的增量變動的架構更易於演進,由於對於開發者來講,變動範圍相對更小。對部署而言,增量變動指業務功能的模塊化和解耦水平,以及它們是如何映射到架構中去的。示例以下。
假設有一個小工具商家 PenultimateWidgets,該商家採用微服務架構和一些現代工程實踐運營着一個產品目錄頁面。該頁面的一個功能是讓用戶給出小工具的星級評分。PenultimateWidgets 的其餘一些業務也須要評分,好比客服表明、物流服務商評價等,因此這些業務共享星級評分服務。某天,該服務的開發團隊發佈了新版本,新版本容許用戶給出半星評價(一個小而重要的升級)。其餘須要評分的服務不須要強制升級,而能夠逐漸地在合適的時候遷移到新版本。PenultimateWidgets 的 DevOps 實踐採用了架構級別的監控,不只監控各個服務,還監控服務與服務之間的路由。當運營人員觀察到在給定時間內沒有請求路由至特定服務時,他們自動將該服務從體系中移除。
這是一個在架構級別進行增量變動的例子:若有須要,原服務和新服務能夠並存。團隊能夠在適當(不太忙或必要)的時候完成遷移,當全部遷移都完成時,舊版本的服務將會做爲垃圾被自動回收。
增量變動的成功須要一些持續交付實踐的配合。並非任何狀況都須要全部這些實踐,但一般它們會一塊兒發生。第 3 章將討論實現增量變動的方法。
一旦架構師選擇了重要的架構特徵,他們會把變動引導進入架構,以保護這些重要特徵。爲此,咱們借用演化計算中的一個概念:適應度函數。該函數是一種目標函數,用於計算潛在的解決方案與既定目標的差距。在演化計算中,適應度函數決定一個算法是否在持續提高。換句話說,隨着每一個算法變體的產生,基於設計者對算法「適應度」的定義,適應度函數決定每一個變體的「適應程度」。
對於演進式架構,隨着架構的演進,咱們有着相似的需求。咱們須要評估機制,來評估變化對架構重要特徵的影響,並防止這些特徵隨着時間的推移而退化。適應度函數的隱喻涵蓋多種機制,包括度量、測試和其餘檢驗工具。咱們採用這些機制來確保架構不會以不良方式變動。當架構師肯定了須要保護的架構特徵時,他們會定義一個或多個適應度函數來提供保護。
以往,架構每每要劃出一部分做爲管理活動,最近架構師才接受了經過架構實現變動的思想。架構適應度函數容許在組織需求和業務功能的上下文中制定決策,併爲明晰且可測試的決策奠基了基礎。演進式架構並非毫無約束或不負責任的軟件開發方式。相反,它能夠在高速變遷的業務、嚴謹的系統需求和架構特徵間找到平衡。適應度函數驅動架構設計決策,並引導架構變動適應業務和技術環境的變化。
第 2 章將詳細介紹使用適應度函數指導架構演進。
不存在單獨的系統。世界是一個總體。如何劃分系統邊界取決於討論的主題。
——Donella H. Meadows
古希臘的物理學經過固定點分析宇宙,最終發展成了經典力學。可是到了 20 世紀初,隨着儀器愈來愈精密,現象愈來愈複雜,人們逐漸從經典力學轉向相對論。科學家意識到以前被視爲孤立的現象實際上是相互影響的。自 20 世紀 90 年代起,受到啓發的架構師愈來愈多地將軟件架構視做多維的。持續交付也將運維歸入了軟件架構的範疇。軟件架構師每每關注技術架構,但那只是軟件項目的維度之一。若是架構師想構建可演進的架構,就必須考慮系統中全部會受變化影響的部分。正如物理學所講的,萬物都是相關聯的,架構師深知軟件項目是多維的。
爲了構建能夠不斷演進的軟件系統,架構師不能只考慮技術架構。例如,若是項目包含一個關係型數據庫,那麼數據庫實體之間的結構和關係也會隨着時間的推移而不斷變化。另外,架構師也不但願系統在演進過程當中暴露安全漏洞。這些例子展現了架構的不一樣維度,它們一般在架構中以正交方式交織在一塊兒。部分維度在常見的架構關注點範圍以內(見表 1-1),可是架構維度的意義其實更普遍,它囊括了不少傳統意義上技術架構範疇以外的東西。每一個項目都有許多維度,架構師在考慮架構演進時必需要想到。下面是一些影響現代軟件架構演進能力的常見維度。
技術
架構中的實現部分:框架、依賴的庫和實現語言。
數據
數據庫模式、表格佈局、優化計劃等。一般由數據庫管理員(DBA)處理這類架構。
安全
定義安全策略、指導方針和指定工具來幫助發現缺陷。
運維與系統
關注架構如何映射到現有的物理或虛擬的技術設施中,包括服務器、機器集羣、交換機、雲等。
以上每一個視角構成一個架構維度——爲了支持特定視角而有意進行的劃分。這裏架構維度的概念涵蓋了傳統的架構特徵和其餘有助於構建軟件系統的因素。隨着問題和周遭環境的改變,咱們想保護架構不磨損,每一個維度都提供給咱們一個審視架構的視角。
從概念上劃分架構的方法有不少,好比 IEEE 的軟件架構定義中的 4+1 視圖模型。它關注不一樣角色的不一樣視角,將整個系統劃分紅了邏輯視圖、開發視圖、進程視圖和物理視圖。在著名的《軟件系統架構》一書中,做者給出了軟件架構的視點目錄,它涵蓋了更多角色。相似地,Simon Brown 的 C4 建模符號也從概念上幫助組織區分關注點。不過,本書沒有嘗試建立任何維度分類,而是識別那些現有項目中的維度。從實用角度來看,不論如何對關注點進行分類,架構師都須要保證這些維度不磨損。不一樣的項目有不一樣的關注點,這致使每一個項目都有特定的維度。對於新項目,以上任何技術都能提供有用的看法,可是對於現有的項目,咱們必須處理眼前的實際狀況。
按照架構的維度思考,經過評估重要維度對變化的響應,架構師能夠分析不一樣架構的演進能力。隨着系統與互相沖突的問題(伸縮性、安全性、分佈式、事務性等)關聯得愈來愈緊密,架構師必須跟蹤更多的維度。只有結合全部這些重要維度,思考系統將如何演進,才能構建出能夠不斷演進的系統。
項目的整個架構範圍由軟件需求和其餘維度構成。當架構和整個體系隨着時間的推移一塊兒演進時,咱們可使用適應度函數來保護架構特徵,如圖 1-3 所示。
圖 1-3:架構由需求和其餘維度構成,每一個維度都受適應度函數保護
在圖 1-3 中,架構師肯定了可審計性、數據、安全性、性能、合法性和伸縮性是該應用的關鍵架構特徵。隨着業務需求不斷變化,每一個架構特徵都經過適應度函數來保護其完整性。
咱們強調架構總體的重要性,但也應意識到,技術架構模式及相關議題也是架構演進的很大一部分,好比耦合和內聚。第 4 章將討論技術架構耦合對演進能力的影響,第 5 章將討論數據耦合的影響。
耦合不僅和軟件項目的結構元素有關。近來,不少軟件公司認識到了團隊結構對架構的影響。咱們將討論軟件中的各種耦合因素,可是團隊結構的影響出現得如此早且頻繁,咱們須要立刻就此展開討論。
1968 年 4 月,梅爾文 • 康威在《哈佛商業評論》上發表了一篇名爲「How Do Committees Invent?」的論文。在這篇論文中,康威提出:社會結構,特別是人與人之間的溝通途徑,將不可避免地影響最終的產品設計。
康威描述道,在設計的最初階段,人們首先須要高瞻遠矚地思考如何將職責劃分爲不一樣的模式。團隊分解問題的方式會左右他們以後的選擇,這即是康威定律。
在設計系統時,組織所交付的方案結構將不可避免地與其溝通結構一致。**
——梅爾文 • 康威
正如康威所描述的,當技術人員將問題分解成更小的塊,使其更易於委派時,就會產生協調問題。不少組織爲了解決協調問題,會設置正式的溝通結構或是創建森嚴的等級制度,但這樣的解決方案每每是僵化的。好比,按照技術職能(用戶界面、業務邏輯等)劃分的團隊,他們在處理常見的跨職能問題時協調的開銷就會增長。那些從創業團隊跳槽到跨國公司工做的人,極可能會經歷兩種大相徑庭的文化:前者很是靈活且適應性很強,然後者的溝通結構每每是僵化的。康威定律一個很好的例子就是修改兩個服務間的契約。若是一方想修改服務,而這須要另一方的贊成和配合,那麼這件事就可能變得很難。
在論文中康威特別提醒軟件架構師,不要只關注軟件架構和設計,還應關注團隊之間委派、分配和協調工做的方式。
在不少組織中,團隊是根據職能來劃分的。示例以下。
前端開發團隊
負責用戶界面(UI)技術(如 HTML、移動端和桌面端)。
後端開發團隊
專門構建後端服務(有時還包括 API 層)。
數據庫開發團隊
專門構建存儲和邏輯服務。
在這樣的組織中,管理層從人力資源的角度簡單地按照職能劃分團隊,沒有充分考慮工程效率。雖然每一個團隊都有其擅長的領域(好比構建一個視圖,增長一個後端 API 或服務,或者開發一個新的存儲機制),可是當須要發佈新的業務功能或特性時,三個團隊都要參與其中。各個團隊一般都會針對眼前的任務優化效率,而不是針對那些更抽象的戰略業務目標(特別是有工期壓力時)。這會致使各團隊每每專一於交付各自的組件,而不關注端到端的特性價值,致使這些組件可能沒法高效協做。
在這樣的團隊編制下,因爲每一個團隊都在不一樣的時間忙於本身的組件,所以那些依賴全部團隊的特性須要花費更長的時間。例如,修改目錄頁這樣常見的業務變動涉及 UI、業務規則和數據庫模式的變動。若是每一個團隊都各自爲戰,那麼他們必須協調時間表,這將增長實現該特性所需的時間。這個例子很好地解釋了團隊結構對架構和演進能力的影響。
康威在論文裏提到:「每當新的團隊組建,其餘團隊的職責範圍會縮小,可以有效執行的可選設計方案也會隨之變少。」換句話說,人們很難改變其職責範圍外的事情。軟件架構師須要時刻關注團隊的分工模式,從而使架構目標和團隊結構保持一致。
不少構建架構(如微服務)的公司圍繞服務邊界構建團隊,而不是按孤立的技術架構來劃分。在 ThoughtWorks 技術雷達中,咱們稱之爲「康威逆定律」。以這種方式組織團隊是理想的,由於團隊結構會影響軟件開發的不少維度,而且會反映問題的大小和範圍。好比,在構建微服務架構時,企業一般會構建與架構相仿的團隊結構,經過打破功能筒倉使每一個團隊都有人能考慮到業務的各個角度和技術的方方面面。
構建與目標系統架構相仿的團隊結構,這樣項目會更容易實現。
PenultimateWidgets 及其逆康威時刻
本書以 PenultimateWidgets 公司爲例。它是排行倒數第二的小工具經銷商,仍是一家大型在線小工具(各類小商品)商家。這家公司正在更新其大部分 IT 基礎設施,包括一些想暫時保留一段時間的遺留系統和正在迭代開發中的新戰略系統。本書將重點介紹 PenultimateWidgets 所遇到的問題以及採起的解決方案。
他們的架構師首先觀察了軟件開發團隊。舊的單體應用採用了分層架構,將展示、業務邏輯、持久化和運維分離開來。而他們的團隊設置也和架構一模一樣:全部前端開發人員在一塊兒工做,後端開發人員和數據庫管理員也各自工做,運維則外包給了第三方。
當開發人員開始在架構(基於具備細粒度服務的微服務架構)上工做時,協調成本急劇增長。由於服務是圍繞領域(例如用戶結算)而不是技術架構來構建的,因此在變動單個領域時將須要各團隊間進行大量的協調。
因而,PenultimateWidgets 採起了康威逆定律,創建了和服務範圍相匹配的跨職能團隊:每一個服務團隊包括服務負責人、幾位開發人員、一位業務分析師、一位DBA、一位質量保障人員和一位運維人員。
本書多處提到了團隊的影響,並會經過示例說明它帶來的結果。
對於演進式架構,一個常見的問題是關於其名稱的:爲何叫做演進式架構而不是別的?好比增量式、持續式、敏捷式、響應式或應急式,等等。這些術語都不夠準確達意。這裏所說的演進式架構包含了兩個關鍵特徵:增量和引導。
持續式、敏捷式和應急式都表達了隨時間不斷變化的概念,這確實是演進式架構的關鍵特徵,可是這些術語都沒能準確地表達出架構將如何變化,或者說指望的架構最終是什麼樣子的。雖然這些術語都隱含着環境變化,可是都沒能涵蓋架構應有的樣子。而在演進式架構的定義中,引導的含義反映了咱們想實現的架構,即咱們的最終目標。
相比可適應這個詞,咱們傾向於演進式,由於咱們對經歷根本性演變的架構感興趣,而不是那些經歷了修修補補後變得愈來愈難以理解、複雜問題頻發的架構。適應意味着解決方案不求優雅或長久,只要能找到方法使系統運做就行。爲了構建能實實在在演進的架構,架構師必須支持真正的變化,而不是權宜之計的考慮。回到咱們的生物學隱喻,演進是這樣一個過程:創建一個適用的並能在其所處的不斷變化的環境中持續運行的系統。系統可能存在個別的適應性,可是架構師應該關心總體可演進的系統。
演進式架構主要由三方面構成:增量變化、適應度函數和適當的耦合。本書餘下的部分將分別討論它們,而後再將它們結合起來,幫助咱們構建和維護支持不斷變化的架構。