讀書筆記《演進式架構》

《演進式架構》

英文版原名:Building Evolutionary Architectures

第1章 軟件架構

演進式架構(Evolutionary Architecture)

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

An evolutionary architecture supports guided, incremental change across multiple dimensions.

http://evolutionaryarchitecture.com/ :

  1. Incremental: Evolutionary architectures are built one part at a time, with many different increments. Speed to the next increment is key.
  2. Fitness Functions: Every system at different points of their life need to optimise to be "fit" for its environment. Evolutionary architectures make it explicit what "fit" means with as much automation as possible.
  3. Multiple Dimensions: Evolutionary Architectures must support both technical and domain changes

從定義看出演進式架構的三個關鍵:增量變更、引導性變更、多個架構維度

增量變更

增量變更描述了軟件架構的兩個方面:如何增量地構建軟件和如何部署軟件。在開發階段,允許小的增量變更的架構更易於演進,因爲對於開發者來說,變更範圍相對更小。對部署而言,增量變更指業務功能的模塊化和解耦水平,以及它們是如何映射到架構中去的。

增量變更的成功需要一些持續交付實踐的配合。

引導性變更

適應度函數

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

**架構的適應度函數Architectural Fitness Functions **

要理解“架構適應度函數”這個詞語應該還是英文原文(來源)更合適一些:

Evolutionary architecture allows different parts of the system to evolve in the ways most sensible to solve a problem. However, we don’t want the system to evolve in a way that harms some architectural concern. For example, improving performance with caching might accidentally harm security in the process. How can we build guidelines within the architecture to support change but guard specific attributes? Evolutionary computing defines a fitness function as an objectively quantifiable function used to summarise how close a given design solution is to achieving the set aims. When defining an evolutionary algorithm, the designer seeks a “better” algorithm; the fitness function defines what “better” means in this context.

多個架構維度

軟件架構師往往關注技術架構,但那只是軟件項目的維度之一。如果架構師想構建可演進的架構,就必須考慮系統中所有會受變化影響的部分。

爲了構建可以不斷演進的軟件系統,架構師不能只考慮技術架構。

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

爲了構建可以不斷演進的軟件系統,架構師不能只考慮技術架構。

康威定律

在設計的最初階段,人們首先需要高瞻遠矚地思考如何將職責劃分爲不同的模式。團隊分解問題的方式會左右他們之後的選擇,這便是康威定律。在設計系統時,組織所交付的方案結構將不可避免地與其溝通結構一致。

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

小結

演進式架構主要由三方面構成:增量變化、適應度函數和適當的耦合。

書之外的一些參考資料

網上其他讀書筆記參考

  1. 【讀書筆記】Building Evolutionary Architectures
  2. 懶人喫書:Building Evolutionary Architectures
  3. building-evolutionary-architectures.md

視頻(www.bilibili.com):作者的一些英文演講視頻,先收藏了,:)

  1. Building Evolutionary Architectures – Rebecca Parsons
  2. Building Evolutionary Architectures • Patrick Kua
  3. Evolutionary Architecture & Microservices • Rebecca Parsons
  4. Neal Ford - Evolutionary Software Architectures

PDF:

  1. Building-Evolutionary-Architectures.pdf (英文版pdf)

如何理解架構適應度函數

(以下爲個人理解)

這一章引入了一個令人難以理解的術語——適應度函數,但是這個比較奇怪的“適應度”卻貫穿了整本書,導致我再翻了大半本書之後發現還是沒有明白它在說什麼,所以離開書去找了一些其他的資料來先嚐試理解這個概念。最後還是通過書的官網(http://evolutionaryarchitecture.com/ffkatas/index.html)的英文對“架構適應度函數(Architectural Fitness Functions)”稍有了理解,雖然英語沒學好,但有時候還真是要看英文原版才能理解一些東西。其實可以稍微簡單點理解,架構適應度函數就是一個可衡量架構設計的目標是否達成的工具,或者說是一種驗收標準,由於架構存在各種各樣的特徵(上面說的“多個架構維度”),所以對於一個架構設計是否達成目標就要綜合多方面的因素來進行完整的評估,以制訂這樣一種工具來驗證我們的架構設計、演化是否滿足要求。

第2章 適應度函數

什麼是適應度函數

我們對架構的適應度函數的定義如下:

架構的適應度函數爲某些架構特徵提供了客觀的完整性評估。適應度函數能夠保護系統所需的各種架構特徵。由於業務驅動、技術能力及其他諸多不同因素,系統和組織對架構的具體需求會有很大區別。有些系統要求很高的安全性,有些要求可觀的吞吐量或低延遲,還有一些要求更好的故障恢復能力。這些對系統的考量形成了架構師所關心的架構特徵。從概念上來講,適應度函數體現了系統架構特徵的保護機制。

最終,所謂“適應度函數引導演進式架構”,指的是通過單獨的適應度函數評估單個架構選擇,同時通過全系統適應度函數確定變更的影響。適應度函數共同指出架構中對我們重要的部分,使我們能夠在軟件開發過程中做出各種關鍵又令人煩惱的權衡。

適應度函數將許多已有的概念統一爲一個整體機制,讓架構師可以統一思考許多現有的(往往是臨時的)“非功能性需求”測試。收集重要的架構閾值和需求作爲適應度函數,使得以前模糊又主觀的評價標準變得更加具體。我們利用了大量現有機制來構建適應度函數,包括傳統的測試、監控等工具。當然,並非所有測試都是適應度函數,只有當測試有助於驗證架構問題的完整性時,它纔是適應度函數。

適應度函數分類

  1. 原子適應度函數與整體適應度函數:
    原子適應度函數針對單一的上下文執行,用來校驗架構的某一維度;
    整體適應度函數在共享的上下文中運行,綜合檢驗架構的多個維度,比如安全性和伸縮性。開發人員設計整體適應度函數來保證原子級特性能夠正常地協同工作。
  2. 觸發式適應度函數與持續式適應度函數
    觸發式適應度函數基於特定的事件執行,比如開發人員執行單元測試、部署流水線執行單元測試。也就是說基於構建等事件觸發通過自動化測試來檢測是否滿足架構要求。
    持續式測試不是按計劃執行,而是持續不斷地驗證架構的某些方面,比如事務處理速度。也就是說基於監控等手段來檢測是否滿足架構要求。
  3. 靜態適應度函數與動態適應度函數
    靜態適應度函數的結果是固定的,比如單元測試的二進制結果——成功或失敗。該類型囊括了預定義期望值的適應度函數,比如二進制、數字區間、集合包含等。這類適應度函數通常會用到各種衡量指標。例如,架構師會爲代碼中的方法定義可接受的平均圈複雜度,並通過嵌在部署流水線的度量工具對其進行檢查。
    動態適應度函數依賴基於額外上下文變化的因素。某些值會視具體情況而定,比如在大規模運行的情況下,大多數架構師會採用較低的性能指標。例如,某公司可能基於伸縮性將性能指標設置爲特定範圍內的浮動值——在較大的規模下允許較低的性能。
  4. 自動適應度函數與手動適應度函數
    自動化執行適應度函數測試
    或者某些情況下不得不通過手動的方式來執行適應度函數
  5. 臨時適應度函數
    雖然大多數適應度函數在變更發生時被觸發,但架構師可能想通過時間組件評估適應度。
  6. 預設式高於應急式
    雖然在項目初期,架構師會定義大多數適應度函數,因爲它們闡明瞭架構的特徵,但有些適應度函數在系統開發階段才顯現。架構師無法在開始時就知曉架構的所有重要部分(第6章將討論經典的未知的未知問題),因此必須隨着系統發展不斷確定適應度函數。
  7. 針對特定領域的適應度函數
    某些架構有着特定的關注點,比如特殊的安全或監管需求。例如,一家處理跨國轉賬的公司可能設計特定的持續式整體適應度函數,模仿Simian Army(第3章將介紹)對安全性進行壓力測試。許多問題域都包含能引導架構師確定重要架構特徵的因素。架構師和開發人員應該捕捉這些因素,並將其作爲適應度函數來保證那些重要的架構特徵不會隨着時間出現磨損。

儘早確定適應度函數

將適應度函數的執行結果可視化至明顯的公共區域,能使開發人員記得在日常編碼中考慮它們,保持關鍵部分和相關適應度函數的活力。

對適應度函數進行分類有助於確定設計決策的優先級。如果一個設計決策對某個關鍵適應度函數有特定影響,那麼應該花費更多時間和精力進行探針試驗(時間可控的試驗性編碼工作)來校驗設計的架構。有些團隊採取基於集合的開發方式,它是精益和敏捷流程中的開發實踐,用於同時設計多個解決方案。它以構建多套方案爲代價來換取未來決策的可選方案。

審查適應度函數

適應度函數審查以會議的形式進行,會上主要業務和技術利益相關者會一起討論如何修改適應度函數以滿足設計目標。例如,當市場份額或用戶數量顯著增長時,或者引入新的功能或業務能力時,又或者大規模檢修現有系統時,都必須審查適應度函數。

適應度函數審查大致涉及如下幾點。· 審查已有的適應度函數。· 審查當前適應度函數的相關性。· 確定每個適應度函數的規模或大小的變化。· 確定是否有更好的方法測量或測試系統的適應度函數。· 發現系統可能需要支持的新的適應度函數。

第3章 實施增量變更

可測試性

通過自動化和工具來構建和發佈軟件的機制是演進式架構的前提。

將項目的架構關注點(包括演進能力)轉換爲適應度函數能帶來很多好處:

  1. 適應度函數的結果客觀且可量化
  2. 捕獲所有關注點作爲適應度函數,創造了一致的執行機制
  3. 適應度函數列表便於開發人員設計部署流水線

組合不同類型的適應度函數

❑ 原子適應度函數+觸發式適應度函數
在軟件開發過程中運行的單元測試和功能性測試就屬於此類適應度函數。開發人員運行它們來驗證變更,與此同時,自動化機制(例如部署流水線)通過持續集成保證驗證的及時性。通過單元測試驗證應用架構完整性的某些方面是此類適應度函數的常見例子,例如通過單元測試驗證代碼的循環依賴或圈複雜度。

❑ 整體適應度函數+觸發式適應度函數
整體觸發式適應度函數會作爲集成測試的一部分在部署流水線中運行。開發人員設計它們專門來測試系統的各個方面如何按照定義好的方式進行交互。例如,開發人員可能想知道更嚴格的安全控制對系統伸縮性的影響。架構師設計它們來測試代碼的一些集成特徵,因爲失敗的測試能指出某些架構缺陷。正如所有觸發式測試一樣,開發人員通常在開發過程中、在部署流水線中或持續集成環境中同時執行這些適應度函數。通常這些測試和衡量會有明確的結果。

❑ 原子適應度函數+持續式適應度函數
持續測試作爲架構的一部分運行,開發人員圍繞這些測試開展設計工作。例如,架構師可能關心是否所有REST端點都使用了適當的動詞、合理地處理錯誤並正確地支持元數據,爲此構建了可持續運行的工具來調用這些REST端點(和普通客戶端一樣)並驗證調用結果。這些適應度函數的原子範圍表明它們只測試架構的某個方面,持續性則表現在這些測試作爲系統整體的一部分運行。

❑ 整體適應度函數+持續式適應度函數
全系統持續式適應度函數會不停地測試系統的多個部分。基本上,這種機制代表系統的一個代理(或客戶端),它不斷地評估架構和運維的質量。這類適應度函數在現實世界中的一個突出例子是Netflix的Chaos Monkey。Netflix在設計分佈式架構系統時,在亞馬遜雲上運行其系統設計。但是工程師擔心可能發生奇怪的事情,因爲在雲上他們無法直接控制系統的運行,比如高延遲、可用性和彈性等。爲了消除憂慮,他們創建了Chaos Monkey,並最終演化爲完整的開源項目Simian Army。Chaos Monkey“潛入”亞馬遜數據中心,意想不到的事情發生了,比如延遲上升、可靠性降低,其他混亂也隨之發生。由於Chaos Monkey的存在,每個團隊構建的服務都必須具有回彈性。Conformity Monkey是前面提到的RESTful驗證工具,它按照架構師定義的最佳實踐來檢查每個服務。

需要注意的是,Chaos Monkey並不是按計劃運行的測試工具,它在Netflix的體系中持續運行。它不僅促使開發人員構建強大的系統,還不斷地測試系統的有效性。將這種持續不斷的驗證內置於架構中,使得Netflix成爲了世界上最強大的系統之一。Simian Army是整體持續式適應度函數的一個典範。它同時針對架構的多個部分運行,很好地保持了架構特徵(如回彈性、伸縮性等)。

假設驅動開發和數據驅動開發

在《精益企業》這本書中,Barry O'Reilly介紹了假設驅動開發的現代化過程。在這個過程中,團隊應該利用科學手段,而不是收集正式的需求然後花費時間和資源將功能構建到系統中。一旦團隊創建出應用的最小可行產品(無論是新產品還是維護現有產品),他們便能在構思新功能時建立假設,而不是需求。假設驅動開發的假設是根據假設來檢驗的,什麼試驗可以確定結果以及用什麼驗證假設意味着應用開發的走向。

假設驅動開發需要協調很多動態的部分,包括演進式架構、現代DevOps、變更需求收集和同時運行多版本應用的能力。基於服務的架構(比如微服務)通常通過服務間的智能路由實現不同版本服務的並行。例如,某個用戶可能通過某個特定的服務組訪問應用,而另一個請求可能訪問相同服務中完全不同的一組實例。如果多數服務都包含多個運行實例(例如爲了伸縮性),那麼讓一部分實例運行增強過的功能,並將部分用戶路由到這些功能會變得很簡單。

爲了產生可觀的結果,試驗應該進行足夠長的時間。通常最好找到某種可衡量的方式來確定更好的結果,而不是通過彈出窗口等形式的調查來打擾客戶。例如,某個假設的工作流能否讓客戶用更少的鍵盤輸入和點擊完成任務?在不打擾用戶的情況下將其納入開發和設計的反饋環中,可以構建出更實用的軟件。

第4章 架構耦合

很多架構師認爲耦合是必然之惡,但是如果不依賴其他組件(並與之耦合)又很難構建複雜的軟件。演進式架構注重適當的耦合,即如何確定哪些架構維度間應該相互耦合以最小的開銷和成本最大程度地獲益。

4.2 架構的量子和粒度

物理學:量子是物理實體相互作用時所涉及的最小單位。

架構量子則是具有高功能內聚並可以獨立部署的的組件,它包括了支持系統正常工作的所有結構性元素。

在單體架構中,量子就是整個應用程序,每個部分都高度耦合,因此開發人員必須對其進行整體部署。

相比之下,微服務架構在架構元素之間定義了物理界限上下文,封裝了所有可能變化的部分。這種架構就是爲了增量變更而設計的。在微服務架構中,界限上下文作爲量子邊界,包含了服務所依賴的組件,比如數據庫服務器。它還包括一些架構組件,例如搜索引擎、報表工具及任何有助於交付功能的組件。

4.3 不同類型的架構演進能力

4.3.1大泥團架構

❑ 增量變更對這種架構難以做任何變更。相關的代碼散佈於系統各個角落,這意味着修改其中一個組件將意外地破壞其他組件。修復這些破損會導致更多的破損發生,從而產生無盡的連鎖反應。❑ 通過適應度函數引導變更由於沒有明確定義分區,我們很難爲這種架構構建適應度函數。爲了構建保護功能,開發人員必須確定需要保護的部分,但是在這種架構中,除了低級的函數或類之外不存在任何結構。❑ 適當的耦合這種架構是不當耦合的典型。構建這樣的軟件沒有任何架構優勢。

在這樣的糟糕狀態下,變更困難且成本高。本質上,由於系統各部分間高度耦合,架構量子就是整個系統本身,沒有哪個部分可以輕易改變,因爲牽一髮而動全身。

4.3.2 單體架構

1.非結構化的單體架構

由相互獨立的類互相協調而構成的系統

❑ 增量變更巨大的架構量子阻礙了增量變更,因爲高度耦合要求部署大塊應用。組件之間存在高度耦合,這導致很難單獨部署某個組件,因爲需要變更其他組件。❑ 通過適應度函數引導性變更爲單體架構構建適應度函數很難,但並非不可能。因爲這種架構模式存在了很長時間,可以用隨之發展而來的很多工具和測試實踐來構建適應度函數。然而,常見的引導性變更對象通常會成爲單體架構的致命弱點,例如性能和伸縮性。雖然開發人員很容易理解單體架構,但難以構建良好的伸縮性和性能,這很大程度上源於它固有的耦合。❑ 適當的耦合單體架構除了簡單的類之外幾乎沒有內部結構,其耦合程度類似於大泥團架構。因此代碼某處的變更可能對其中某個較遠的部分產生意想不到的副作用。

2.分層架構

內部高度耦合和外部低耦合

❑ 增量變更開發人員發現變更這種架構很容易,特別是在將變更隔離到現有層的情況下。跨不同層的變更則會帶來協調上的挑戰,特別是在組織人員結構和架構分層類似的情況下(這反映了“康威定律”)。例如,某個團隊能在不打擾其他團隊的情況下替換整個持久層框架,因爲他們可以在明確定義的接口背後完成這項工作。但是,當業務要求變更ShipToCustomer(送貨服務)時,該變更則會影響所有層,於是協調在所難免。❑ 通過適應度函數引導性變更開發人員發現,在一個更加結構化的單體應用中編寫適應度函數更爲容易,因爲這種架構的結構更明顯。同時,將關注點分離到不同層使得開發人員能對更多部分進行隔離測試,便於構建適應度函數。❑ 適當的耦合單體架構的一個優點是易於理解。瞭解設計模式等概念的開發人員能輕易將這些知識應用於分層架構中。這種易理解性很大程度上是因爲開發者能輕鬆地訪問所有代碼。分層架構使得由層定義的技術架構劃分更易於演進。例如,一個設計(並實現)良好的分層架構能讓我們很容易地替換掉數據庫、業務規則或其他任何層,並將副作用減至最小。

3.模塊化的單體架構

模塊化的單體架構中包含功能的邏輯分組及模塊間明確定義的邊界

❑ 增量變更由於開發人員能夠執行模塊化,因此在此類架構中很容易進行增量變更。儘管在邏輯上功能被劃分爲不同的模塊,但如果難以單獨部署包含模塊的組件,那麼架構量子依然會很大。在模塊化單體架構中,組件的可部署程度決定了增量變更的速度。❑ 通過適應度函數進行引導性變更測試、度量及其他適應度函數在這種架構中更容易設計和執行,因爲合理劃分了組件,使得測試模擬和其他依賴於隔離層的測試技術更容易實現。❑ 適當的耦合一個設計良好的模塊化單體架構是適當耦合的好例子。每個組件在功能上是內聚的,組件之間的接口設計良好且耦合度低。

4.微內核架構

通常出現在瀏覽器和集成開發環境(IDE)

微內核架構定義了一個核心繫統,核心系統對外提供API來通過插件豐富其功能。在這種架構中架構量子大小有兩種:一種來自核心繫統,另一種來自插件。架構師通常將核心系統設計成單體應用,並在一些熟知的擴展點爲插件創建鉤子(hook)。我們通常把插件設計成獨立且可單獨部署的組件。因此,這種架構支持積極的增量變更,開發人員可以針對可測試性進行設計,更容易定義適應度函數。從技術耦合的角度來看,架構師往往將此類系統設計成低耦合,以保持插件相互獨立,從而簡化它們。

微內核架構的主要挑戰圍繞着契約,它是某種形式的語義耦合。爲了發揮作用,插件必須和核心繫統進行雙向信息傳遞。只要插件不需要互相協調,那麼開發人員就可以專注於插件與核心系統間的信息和版本控制。例如,大多數瀏覽器插件只和瀏覽器交互,而不和其他插件交互。

對於更復雜的微內核系統,插件間的通信無法避免,例如Eclipse Java IDE。除了與文本文件交互之外,Eclipse的核心不支持任何特定語言。所有複雜行爲都是通過插件間的通信實現的。例如,在調試過程中編譯器和調試器必須緊密協調。因爲插件不應依賴其他插件工作,所以核心系統必須處理插件間的通信,這使得協調契約和版本控制等基本任務變得複雜。這個級別的隔離能讓系統幾乎無狀態,雖然很理想,但通常不太可能實現。例如,在Eclipse中,插件運行通常依賴於其他插件,這導致圍繞插件架構量子層面的傳遞依賴於管理。

通常,微內核架構包含一個註冊表來跟蹤安裝的插件及其所支持的契約。在插件間建立明確的耦合加重了系統各部分間的語義耦合,進而導致架構量子變大。

微內核架構廣泛應用於IDE工具,它也能應用於各種商業應用。例如,設想一家保險公司,其理賠的標準業務規則應用於整個公司,但是每個州可能有特別的法規。將該系統構建成微內核系統使得開發人員可以按需支持新的州,並在不影響其他州的情況下升級各州的行爲,這得益於插件與生俱來的隔離性。

如果難以通過插件使技術架構演進,那麼微內核架構是個不錯的選擇。由完全獨立的插件組成的系統更易於演進,因爲插件之間不存在耦合。但依賴彼此協作的插件會增加耦合,進而阻礙系統演進。如果使用彼此交互的插件來設計系統,那麼你還應該通過消費者驅動的契約模型構建適應度函數來保護那些集成點。微內核架構的核心繫統通常很龐大,但是很穩定,因爲大部分的變更應該發生在插件上(除非架構師將應用劃分得很差)。因此,增量變更很簡單:部署流水線觸發對插件的變更並對其進行驗證。

架構師通常不會在微內核技術架構中包含數據依賴,因此開發人員和數據庫管理員必須單獨考慮數據的演進能力。將每個插件視爲限界上下文可以提高該架構的演進能力,因爲這樣可以降低內部耦合。例如,如果所有的插件將同一個數據庫用作核心系統,那麼開發人員必須注意插件在數據級別發生耦合的情況。如果各個插件完全獨立,那麼這樣的數據耦合是不會發生的。

從架構演進的角度來看,微內核架構的理想特徵如下所示。

❑ 增量變更一旦完成了核心系統,大多數行爲應來自插件。如果插件都是獨立的,那麼增量變更會更容易。

❑ 通過適應度函數進行引導性變更通常在這種架構中構建適應度函數很簡單,因爲核心系統和插件是相對獨立的。開發人員分別爲核心系統和插件維護兩套適應度函數。核心適應度函數守護核心系統的變更,包括伸縮性等部署問題。插件測試通常更簡單,因爲對領域行爲的測試是隔離的。爲了便於測試插件,開發人員需要很好地模擬核心繫統。

❑ 適當的耦合微內核模式明確定義了這種架構的耦合特徵。從耦合的角度來看,構建獨立的插件使變更變得不重要。協調相互依賴的插件則更難。開發人員應該通過適應度函數來將相互依賴的組件正確地集成。

此類架構還應包含一些整體適應度函數來確保開發人員維持關鍵的架構特徵。例如,單獨的插件可能影響某個系統屬性,比如伸縮性。因此,開發人員應該計劃構建一套集成測試,把它作爲整體適應度函數。當系統中存在相互依賴的插件時,開發人員還應該構建整體適應度函數來確保契約和消息的一致性。

4.3.3 事件驅動架構

代理模式
代理模式的事件驅動架構在構建強大的異步系統時存在一些設計挑戰。例如,由於缺少集中的中介,很難進行協調和錯誤處理。由於架構各部分高度分離,開發人員必須通過架構還原業務流程的功能內聚。因此,像事務這樣的行爲將更難實現。

中介模式
事務性的協調是中介架構的主要優勢。中介能保證流程的正確性,並生成一條單一狀態消息發送給受保人。

4.3.4 服務導向架構

1.企業服務總線驅動的SOA
ESB驅動的SOA的標誌是消息總線,它負責以下各類任務。❑ 中介和路由消息總線能夠定位服務並與服務通信。通常,消息總線會維護一張註冊表,涵蓋服務的物理地址、協議以及調用服務所需的其他信息。❑ 流程編排與編制消息總線將企業服務組合到一起並管理任務,例如服務調用順序。❑ 消息增強和轉換集成總線的優勢之一是能夠代表應用處理通信協議及其他信息的轉換。例如,支持HTTP協議的服務A(ServiceA)想調用僅支持RMI/IIOP協議的服務B(ServiceB)。當需要此類轉換時,開發人員可以配置消息總線,在無形之中完成此類消息轉換。ESB驅動的SOA的架構量子很大。它基本包含了整個系統,和單體應用差不多,但由於它是分佈式架構,因此更爲複雜。在ESB驅動的SOA中進行演進式變更非常困難,因爲在促進服務複用的同時,其服務分類方法會阻礙普通的變更。例如,在某個SOA中,商品結賬(CatalogCheckout)這一領域概念被打散到了整個技術架構中。僅變更商品結賬就需要架構各部分進行協調,這些部分往往由不同的團隊負責,從而會產生協調摩擦。

❑ 增量變更雖然對複用和隔離資源有完善的技術服務分類方法,但這種架構卻嚴重地阻礙了對業務領域進行最常規的變更。大多數SOA團隊都是按照架構劃分的,這導致進行常規的變更需要大量的協調工作。而且ESB驅動的SOA也是難以操作的。通常它由多個物理部署單元組成,這給協調和自動化帶來了挑戰。沒人會爲了敏捷性和操作的易用性而採用企業服務總線。❑ 通過適應度函數進行引導性變更在ESB驅動的SOA中,通常很難進行測試。各部分都不完整,都是某個巨大工作流的一個環節,通常無法單獨測試它們。例如,某個爲了複用而設計的企業服務,測試其核心行爲通常很困難,因爲它可能只是各個工作流的一部分。爲其構建原子適應度函數幾乎不可能,這導致需要大規模的整體適應度函數進行端到端測試來完成大部分驗證工作。

❑ 適當的耦合從潛在的企業級複用來看,這種奢侈的分類方法是合理的。如果開發人員可以準確地提煉出每個工作流中可複用的精華,那麼最終他們就能夠一勞永逸地構建出企業的所有行爲,而將來的應用開發就變成了連接現有服務。構建ESB驅動的SOA的目標不是爲了系統各部分能夠獨立演進,所以在這方面它表現得非常糟糕。針對分類複用的設計,損害了它在架構級別進行演進變更的能力。

2.微服務架構
將持續交付的工程實踐和限界上下文的邏輯劃分相結合,便形成了微服務的思想基礎以及架構量子概念。

4.3.5 “無服務”架構

BaaS(後端即服務)

FaaS(功能即服務)

4.4 控制架構量子大小

架構量子的大小很大程度上決定了開發人員進行演進式變更的難易程度。

架構演進的結構限制取決於開發人員處理耦合和功能內聚的水平。如果開發人員構建出的模塊化組件系統具有明確定義的集成點,那麼演進會更容易。

架構量子越小,架構的演進能力越強。

JDepend——一個用於分析依賴的開源工具。

第5章 演進式數據

5.1 演進式數據庫設計

數據庫的演進式設計指開發人員能夠根據需求的不斷變化來構建數據庫結構並使其演進。

開發人員必須以和代碼變更相同的方式處理數據庫結構的變更,它們必須是經過檢驗的、版本化的和增量的。

❑ 經過檢驗的
爲了保證穩定性,DBA和開發人員應該嚴格測試數據庫模式的變更。如果開發人員使用了數據映射工具,例如對象關係映射器(ORM),那麼爲了使映射關係和數據庫模式保持同步,他們應該考慮爲此添加適應度函數。
❑ 版本化的
開發人員和DBA應該對數據庫模式和那些使用它的代碼一同進行版本控制。源代碼和數據庫模式是共生的,缺一不可。人爲分離這兩種必然耦合的事物的工程實踐將導致低效。
❑ 增量的
和代碼變更一樣,變更數據庫模式應該是漸進的,即隨着系統的演進而增量地進行。現代工程實踐往往使用自動化的遷移工具,從而避免手動更新數據庫模式。

很多數據庫重構技術通過在重構的過程中創建過渡階段來避免時間問題。使用該模式,開發人員會設置開始狀態和結束狀態,它們會在轉變過程中分別維持新、舊兩種狀態。這個過渡狀態允許向下兼容,同時還給企業內的其他系統足夠的時間來跟上變化。

5.2 不當的數據耦合

數據庫廠商在世界各地的企業中擁有一支祕密軍隊,他們效忠於數據庫廠商而不是其僱主。在這種情況下,DBA會忽略那些不是來自數據庫廠商的工具和開發組件。其結果便是工程實踐的創新水平停滯不前。

事務是耦合的一種特殊形式,因爲事務性行爲不會在傳統的、以技術架構爲中心的工具中出現。架構師能夠輕鬆地通過各種工具確定類的傳入和傳出耦合。但是,確定事務上下文範圍要困難得多。正如數據庫模式的耦合會妨礙演進一樣,事務性耦合以具體的方式將各個組成部分綁定在一起,使得難以演進。
出於各種各樣的原因,事務出現在業務系統中。第一,業務分析師青睞“事務”這個概念(一種在某些情況下暫停世界的簡單操作),而忽略技術上的挑戰。在複雜系統中很難進行全局協調,而事務就是某種形式的全局協調。第二,我們通常能從事務邊界中看出在實現層面上業務概念是如何耦合在一起的。第三,DBA可能管理事務上下文,這導致在技術架構中拆分數據來耦合將難以協調。
在嘗試將事務性很強的系統轉換爲不當的架構模式(如微服務)時,開發者會將事務視爲耦合點,從而帶來了繁重的解耦負擔。

架構師在考慮架構變更時應將事務邊界考慮在內。

不要僅按字面意思來理解微服務,對每個服務而言,小並不是必需的,能捕獲有用的限界上下文才是關鍵。

第6章 構建可演進的架構

  1. 識別受演進影響的架構維度
  2. 爲每個維度定義適應度函數
  3. 使用部署流水線自動化適應度函數

雖然有些適應度函數會在項目初期自然顯現,但還有一些適應度函數在架構經受壓力時纔會顯現。架構師尤其需要注意那些非功能性需求被破壞的情況,並通過適應度函數更新架構來避免可能出現的問題。

第7章 演進式架構的陷阱和反模式

項目中有兩種錯誤的工程實踐——陷阱和反模式。很多開發人員使用行話“反模式”來表示“不好的模式”,但其真實含義更加微妙。軟件的反模式包含兩層含義。首先,反模式是一種實踐,開始看起來不錯,但結果證明是錯的。其次,大多數反模式都有更好的替代方案。很多反模式只有在事後才被架構師注意到,因此很難避免。陷阱表面上像是個好主意,但很快便顯露出缺點。本章將詳細介紹它們。

7.1 技術架構

7.1.1 反模式:供應商爲王

與其淪爲供應商爲王反模式的受害者,我們不如將供應商產品視爲集成點。開發人員可以在集成點間構建防腐層,從而避免架構受到供應商工具變更的影響。

7.1.2 陷阱:抽象泄漏

底層抽象破壞會導致意外的災難,即原始抽象泄漏,它是技術棧日漸複雜帶來的副作用之一。如果最底層的某個抽象出現錯誤會怎樣呢?例如,看似無害的數據庫調用帶來的意外副作用。由於層層抽象的存在,錯誤將蜿蜒至技術棧頂層,或許會沿着路徑轉移,最終在UI上表現爲深度嵌套的錯誤信息。隨着技術棧愈發複雜,調試和取證分析變得更加困難。
始終保持對當前抽象層以下至少一個抽象層的完全理解

7.1.3 反模式:最後10%的陷阱

客戶需求的10%雖有實現的可能,但是極其困難。因爲工具、框架或編程語言都沒有提供相應功能。

這些工具(通過一些非正常的方式實現需求)仍然無法完全解決問題,我們稱其爲“最後10%的陷阱”,即所有項目都存在缺憾。

7.1.4 反模式:代碼複用和濫用

微服務避免代碼複用,遵循重複優於耦合的理念。

7.1.6 陷阱:簡歷驅動開發

簡歷驅動開發的陷阱——爲了用這些知識豐富自己的簡歷而選擇框架和庫。不要爲了架構而構建架構,構建架構是爲了解決問題。

7.2 增量變更

7.2.1 反模式:管理不當

十年前,操作系統是非常昂貴的商品。數據庫服務器、應用服務器和運行應用的整個基礎設施也是如此。爲了應對這些現實世界的壓力,架構師通過設計架構來最大程度地共享資源。當時湧現了很多架構模式,例如SOA。在那樣的環境中,一種常見的管理模式應運而生,它將最大化共享資源作爲節省成本的措施。很多工具的商業動機都是從這種趨勢中發展而來,例如應用服務器。然而從開發的角度來看,由於無意的耦合,在同一臺主機上打包多個資源並不可取。無論共享資源間隔離得多麼好,資源競爭終將出現。

當開發人員能夠不費成本(金錢或時間)地創建虛擬機和容器資源時,看重單一解決方案的管理模式就變得不適用了。微服務領域出現了一種更好的方式。微服務架構的一個常見特徵就是支持異構的環境,各個服務團隊可以選擇適合的技術棧來實現他們的服務,而不用按照企業標準進行統一。這與傳統方式截然相反,因此當傳統企業的架構師聽到這個建議時會退縮。然而,大多數微服務項目的目標並不是武斷地選擇不同的技術,而是根據具體問題選擇適當的技術。

7.2.3 陷阱:發佈過慢

持續交付追求數據驅動的結果,從指標數據中學習如何優化項目。開發人員必須衡量事物從而瞭解如何優化。生產週期是持續交付的一個關鍵指標,和交付週期相關。交付週期是指從一個想法開始到它在軟件中實現所耗費的時間。然而,交付週期中包含很多主觀活動,例如估算、排列優先級等,使其成爲了一個糟糕的工程指標。因此持續交付跟蹤生產週期,即啓動和完成單位工作所用的時間,這裏指軟件開發。生產週期從開發人員着手開發某個新功能起開始計時,當該功能在生產環境中運行時停止計時。其目標是衡量工程效率,持續交付的關鍵目標之一便是縮短生產週期。

7.3 業務問題

7.3.1 陷阱:產品定製

❑ 爲每個客戶定製
在這個場景中,銷售人員在緊迫的時間內承諾實現特定版本的功能,迫使開發人員使用版本控制分支或標籤技術來跟蹤版本。
❑ 永久的功能開關
第3章介紹過功能開關,有時將它戰略性地用於構建永久的定製功能。開發人員可以使用功能開關來爲不同客戶創建不同的版本,或創建“免費”版產品,讓用戶付費解鎖高級功能。
❑ 產品驅動定製化
有些產品甚至可以通過UI來完成定製。在這種情況下,定製功能是應用的永久部分,需要和其他所有產品功能受到同樣的維護。

7.3.2 反模式:報表

很多微服務架構通過分離行爲來解決報表問題,而微服務的隔離有利於分離但不利於整合。通常構建這類架構時,架構師使用事件流或消息隊列來向領域“記錄系統”數據庫填充數據,每個記錄系統嵌在服務架構量子中,使用最終一致性而不是事務行爲。一些報表服務也會監聽事件流,向針對報表優化過的非規範化數據庫中填充數據。

消除因混合領域和報表引起的不當耦合,使得每個團隊可以專注於更加具體且簡單的任務。

7.3.3 陷阱:規劃視野

人們在規劃和文檔上投入的時間和精力越多,就越可能保護其中的內容,即便有證據表明它們不準確或過時了。

謹防長期規劃,因爲它會迫使架構師做出的決策不可逆轉,同時找到方法來保證多個可選方案。將大型項目分解成更小的早期可交付物,以測試架構選型和開發基礎設施的可行性。在通過最終用戶反饋驗證所用技術確實適用他們試圖解決的問題之前,架構師應該避免在實際構建軟件之前採用需要大量前期投入的技術,例如大型許可和支持合同。

第8章 實踐演進式架構

8.1 組織因素

8.1.1 全功能團隊

以領域爲中心的團隊應該是全功能的,這意味着每個項目角色都由該項目組成員承擔。以領域爲中心的團隊,其目標是消除運營摩擦。換句話說,團隊擁有負責設計、實現和部署其服務的所有角色,其中還包括傳統上單獨的角色,例如運維。

全功能團隊的目標之一便是消除協調摩擦。傳統的團隊彼此獨立,開發人員通常需要等DBA做出變更或等運維人員提供資源。同一個團隊包含各種角色能消除不同團隊協調所產生的偶然摩擦。

由於資源受限,很多公司無法如願地組建全功能團隊。在這種情況下,項目間可以嘗試共享受限的資源。例如,與其爲每個服務配備一名運維工程師,或許他們可以在幾個不同的團隊間輪轉。

通過圍繞領域組建架構和團隊,現在可以由同一個團隊處理常見的變更單元,從而減少了團隊間的摩擦。

8.1.2 圍繞業務能力組織團隊

圍繞領域組織團隊即意味着圍繞業務能力組織團隊

按照業務能力而非職能來組織團隊。

8.1.3 產品高於項目

圍繞產品而非項目來組織團隊工作,是很多公司切換團隊工作重點的一種機制。

通常,開發人員和他們運行的代碼的間接層越多,他們與代碼之間的聯繫就越少。有時這會導致在不同團隊間產生對立心態,這並不奇怪,因爲很多組織結構催生了員工間的衝突。

通過將軟件視爲產品,公司能在三個方面實現轉變。第一,與項目的生命週期不同,產品的生命更長久。全功能團隊(通常基於康威逆定律)與產品保持聯繫。第二,每個產品都有一個負責人,他會主張在體系中使用該產品,並管理其需求。第三,由於是全功能團隊,團隊擁有產品所需的各種角色,例如業務分析師、開發人員、質量保障人員、DBA、運維人員等。

亞馬遜的“兩個比薩”團隊:亞馬遜以其產品團隊的組織方式而聞名,他們稱之爲“兩個比薩團隊”。其理論是,兩個大的比薩就夠任何一個團隊吃了。這種劃分方式背後的動機更多是爲了溝通方便而不是控制團隊大小,因爲在更大的團隊中,成員必須和更多的人溝通。每個團隊都是全功能團隊,並且都奉行着“誰構建,誰運行”的理念,這意味着每個團隊全權負責其服務,包括運維工作。

構建全功能團隊可以防止不同團隊間的相互指責,並讓團隊產生主人翁意識,激勵團隊成員做到最好。

8.1.4 應對外部變化

在消費者驅動的契約中,信息消費者構建一組測試來封裝他們對服務提供者的需求,並將這些測試移交給服務提供者,服務提供者承諾始終讓這些測試通過。因爲這些測試覆蓋了服務消費者所需的信息,所以服務提供者能以不破壞這些適應度函數的方式演進。在圖8-1所示的場景中,服務提供者除了運行自己的測試組件外,還代表所有消費者運行這些契約測試。我們將採用這類適應度函數簡單稱爲工程安全網。當可以輕鬆構建適應度函數來維護集成協議的一致性時,就不應該手動處理這類雜事。

8.1.5 團隊成員間的連接數

人與人之間的連接數=n*(n-1)/2

當團隊人數達到20時,他們必須管理190個連接;當組員增長到50個時,連接數則增長爲驚人的1225。因此,構建小型團隊是爲了減少溝通連接。並且,爲了消除不同團隊間協作所產生的人爲摩擦,這些小型團隊需要是全功能團隊。每個團隊無須瞭解其他團隊所做的事情,除非團隊之間存在集成點。即便如此,也應該使用適應度函數保證集成點的完整性。

儘量減少開發團隊之間的連接數。

8.2 團隊的耦合特徵

8.2.1 文化

文化(名詞):特定人羣和社會的想法、習俗及社會行爲。

如果團隊不習慣改變,那麼架構師可以引入實踐來優先應對這一點。例如,當團隊考慮採用某個新的庫或框架時,架構師可以讓團隊通過快速試驗來進行明確的評估,看看新的庫或框架會引入多少額外的耦合。工程師是否可以輕鬆地在該庫或框架之外編寫和測試代碼,又或者新的庫或框架是否需要配備額外的運行環境,而拖慢開發週期?

事不過三,三則重構:第一次做某件事時,你儘管去做。第二次做類似的事時,你會對重複猶豫,但無論如何你還是重複這件事。第三次再處理類似的事時,你應該將其重構。

8.2.2 試驗文化

8.3 首席財務官和預算

在演進式架構中,架構師追求合適的量子大小和對應成本之間的最佳點。每個公司情況各異。例如,市場迅猛發展,公司可能需要更快的變更速度,因此需要更小的架構量子。記住,新一代架構的出現速度與生產週期成正比,架構量子越小,生產週期越短。

8.5從何開始

8.5.1 容易實現的目標

如果組織需要早期成功來證明這種方法,架構師可以選擇最簡單的問題來凸顯演進式架構方法。通常,這是系統中在很大程度上已經解耦的一部分,而且最好不在任何依賴的關鍵路徑上。團隊可以通過增強模塊性和降低耦合來展示演進式架構的其他方面,如適應度函數和增量變更。構建更好的隔離可以使測試和適應度函數更具針對性。更好地隔離可部署單元使得構建部署流水線更容易,併爲構建更強大的測試提供了平臺。

這種“最簡單者優先”的方法將風險降到了最低,但可能犧牲價值,除非團隊有幸找到既容易解決、價值也高的問題。

8.6 演進式架構的未來

8.6.2 生成式測試

在很多函數式編程社區中,生成測試是廣受歡迎的常見實踐。傳統單元測試包含對每個測試用例結果正確與否的判斷。然而,通過生成測試,開發人員運行大量測試並抓取結果,然後對結果進行統計分析來查找反常的行爲。例如最常用的邊界值檢查,傳統單元測試檢查已知的數字臨界點(負數、不斷增加的數字等),但無法覆蓋意外的少數情況。生成測試檢查每一個可能的數值並報告失敗的少數情況。

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