l

2017年1月20日 星期五

好的軟體架構要具備那些特性?

Jan. 19 01:53~3:02; 10:51~13:12

屏幕截图 2017-01-19 13.11.05

▲陰陽調和,流程面與結構面都要兼顧

 

今天介紹《Software Architecture in Practice, 3rd》提到好的軟體架構應該具備的特性,書中分別從流程(process)結構(structure)來討論這個問題。

***

流程面

  1. 架構應該由一位架構師或由技術領導所帶領的架構師小組所設計,如此可確保架構的概念完整性技術一致性。這一點看起來好像傳統瀑布式流程的作法,但書上特別強調此點適用於敏捷開發、開源軟體開發、以及瀑布式開發。與傳統瀑布式作法不同的地方在於架構師不是把架構圖設計好之後丟給開發團隊就可以閃人了,而是需要與開發團隊緊密合作。
  2. 架構師應該持續維護一份排序過的品質屬性需求quality attribute requirement,也就是傳統所說的非功能需求)清單,作為設計取捨或妥協的重要參考依據。架構設計需要考量的品質需求很多,例如可用性、可靠性、效能、延展性、可擴充性、安全性等,但並不是所有的品質因素都同等重要。決定這些因素的優先順序,對於架構設計的取捨非常重要。例如,如果安全性是最優先考量的品質因素,可能就需要在效能上或經濟上做出某種程度的妥協。
  3. 架構的產出或文件應該依據不同觀點或視角(view)來記錄,這些文件必須依據專案時間表滿足開發人員與重要利害關係人的顧慮。也就是說專案剛開始只需要有最少所需的文件即可,這些文件隨著專案進行再逐步展開。這一點很有敏捷開發的味道,和傳統瀑布開發的作法很不同。
  4. 架構應該在專案早期依據其實現系統重要品質屬性的能力加以評估,並且視情況定期重複評估以確保需求或外在環境改變導致架構上的改變並不會使得原本的架構變得英雄無用武之地。
  5. 架構本身應該支援增量式實作以儘早發現問題,並可避免傳統瀑布式開發在專案晚期一口氣整合所有東西的作法。支援增量式實作的一種常見方法就是先產生只支援最小功能的架構骨架做為團隊溝通之用,隨著專案進行這個骨架再慢慢「長大成人」,並且依據需要加以整骨(重構)以避免在逐步成長的過程中不小心長歪掉。

上述內容在2003年出版的《Software Architecture in Practice, 2nd》書中並沒有那麼濃厚的敏捷開發或是迭代與增量開發的味道,2013年出版的《Software Architecture in Practice, 3rd》在這部分有隨著敏捷開發的普及而加以調整。但如果真的要談敏捷開發與軟體架構的關係可能三天三夜也談不完,例如:

  • 敏捷開發團隊是否需要有職稱為「架構師」的團隊成員或是需要組織「架構師團隊」?還是落實「每個開發者都是架構師」的高大上理念?
  • 敏捷開發需要產出那些架構文件?以何種形式記錄?
  • 使用者故事對照(User Story Mapping)所產出的「walking skeleton」—可用的產品需求骨架—如何作為設計「架構骨架」的參考?
  • 迭代週期介於1~4周,在這麼短的時間內架構如何逐步成長又不會長歪掉?
  • 如何矯正(重構)長歪的架構?

架構在敏捷開發所扮演的角色,以後有機會再慢慢談。

***

結構面

  1. 架構的功能需求應該依據資訊隱藏(information hiding)關注點分離(separation of concerns)的原則分派到合適的模組當中。每個模組有明確的介面與外界溝通,透過介面隱藏模組內的細節與異動。在大部分的情況之下不同開發團隊可以依據介面互相獨立運作。
  2. 除非你的需求是前人從來都沒遇到過的(有可能但機會很低),否則品質屬性應該透過廣為人知的架構模式戰術(書中第13章介紹)來達成。翻成白話文就是「架構設計不需要自創武功」。
  3. 架構永遠不應相依於特定版本的商業產品或工具。如果無法避免,至少要做到能夠直覺且不需太高成本就可以改用其他版本。現今的軟體開發絕對不可能什麼東西都自己做,所以架構勢必會使用到外部軟體或工具。如果架構的核心過於依賴這些外部軟體或工具,當它們改版的時候,原先設計的架構就可能無法正常運作。這一點不是很好拿捏,如果使用到外部軟體或工具的時候全部都還要再設計一層來隔離改變,那麼設計與實作成本會很高。如果架構核心直接使用外部軟體或工具,又很容易因為外部軟體或工具改變而「帶屎」自己的系統。有一種觀點是從「軟體穩定性」來考慮,如果你用到的外部系統「很穩定」,那麼直接在架構核心中使用問題就不大。例如,你的架構中需要「日誌」(logging)服務,直接使用log4j這類的已經非常穩定的外部工具通常不會造成很大的問題。但是如果選用非常新奇的工具,則很可能接下來必須不斷處理工具改版的問題,那是非常痛苦的經驗(問問前端Javascript開發人員就知道了XD)。
  4. 區分產生資料與處理資料的模組,如此可提升可修改性因為改變經常被侷限在兩者其中,例如產生資料的演算法改變或是顯示資料的方式改變。當然如果產生資料格是改變一定也會影響到處理資料的模組,但是將兩者分開可以讓這樣的改變被按部就班的修改。
  5. 不要假設靜態模組(module)和動態元件(component)之間存在一對一的關係。例如,同一個模組可能被分派到好幾台電腦上執行,或是在多執行緒的系統中,每一個執行緒可能會使用到多個元件的服務,而每一個元件又是由好記個模組所組成。這一點翻成白話文就是說,架構師需要同時考慮到架構的三種結構:module、C&C、allocation。
  6. 行程(process)要跑在哪一個處理器(processor)應該要可以很簡單地調整,甚至是在執行期間也可以調整。分散式系統與雲端計算很需要這樣的能力。
  7. 架構應該維持少數的元件互動方式即可,也就是系統從頭到尾用相同的方式做相同的事,如此可增加系統的可理解性,減少開發時間,提高可靠性以及可修改性。例如,系統內部的資料交換全部採用JSON格式,不要一下子用JSON,一下子轉成XML,一下子又改用object;又或者是只提供JSON與XML兩種格式自動轉換。通常架構中支援多種互動方式(資料格式或通訊協定)的原因,不外乎「整合」與「相容性支援」這兩個因素。如果你的系統不需要和別人整合,根本不需要支援多種互動方式,因為這樣最省時、省錢。很多web service或microservice可能同時支援不同的通訊協定與格式,主要也是為了服務不同的客戶端。如果是自己系統內部的溝通,實在沒必要設計那麼多種方式搞死自己。另外一個情況是,就算系統本身不需要和別人溝通,但因為改版的原因,之前使用的互動方式已經「不流行」或「不好用了」,因此改用新的互動方式。但是系統要保持向下相容,原有的方式還是要支援,所以存在多種互動方式。例如,Spring framework原本大量使用XML作為設定方式,後來支援更方便的Java annotation,這就是「用不同的方式做相同的事情」的例子。理論上這樣會增加使用者(開發人員)理解與應用架構的困難度,但這也沒辦法,架構演進自然會發生這樣的狀況,但至少可以留意一下,一開始設計的時候不要惡搞自己。
  8. 架構應控制一小組特定資源爭用區(resource contention area),並且明確規範與維持其使用方法。例如,如果網路頻寬是爭用資源,架構師要規範使用指導原則並確保不同的開發團隊都理解與遵守。又例如在即時系統中效能(CPU使用時間)是爭用資源,架構師就要規範每一個執行緒可使用的計算時間。

***

一個好的軟體架構必須兼顧流程面與結構面。結構面在〈什麼是軟體架構?〉中提過可以透過module structure、component-and-connector structure、allocation structure來表達,這也是一般架構師在設計軟體架構的產出物。至於這些結構面的產出物如何融入到流程面,在傳統瀑布開發方法中採用「丟過牆」的方式,透過大量文件交給開發人員實作。這種作法在現今迭代與增量開發流程中顯得不合時宜,但這也是現代架構師的挑戰:「不是閉著眼睛產出一堆開發人員看了滿頭霧水的大部頭架構文件,而是要讓架構設計與開發流程互相搭配」。

***

友藏內心獨白:所以說軟體生命週期管理很重要啊。

沒有留言:

張貼留言