架構設計的深入思考與總結——概述

前言

解決軟件複雜度與規模增長有效的利器總結下來,就是分治、知識、抽象。開發者把問題分割爲規模更下且易於處理的更多子問題,這樣他們就可以利用相似的知識集中的解決這些子問題,並且使用抽象幫助推理與判斷。
從看見樹木,到看到森林,再到看見樹木。

軟件架構是什麼?

參考相關文檔:
Software architecture refers to the high level structures of a
software system and the discipline of creating such structures and
systems. Each structure comprises software elements, relations among
them, and properties of both elements and relations.[1]


The architecture of a software system is a metaphor, analogous to the
architecture of a building.[2] It functions as a blueprint for the
system and the developing project, laying out the tasks necessary to
be executed by the design teams.[3]
                    ————卡內基梅隆軟件工程研究所

簡單來說,軟件架構設計=底層實現細節+高層架構信息。所謂底層和高層本身就是一系列組成的連續體,並沒有清晰的分界線。

如果架構師只畫幾張籠統抽象的圖,而對底層實現細節沒有控制力,這樣的架構就是萬能的架構,沒有任何意義!

我觀察到有些架構師只是畫幾張抽象的圖,以爲這就是架構設計的全部,當時我感到十分懷疑。更多閱讀了《clean architecture》、《恰如其分的軟件架構》書籍,更加堅定了我的想法。軟件宏觀架構信息只是一部分,缺失了重要的底層細節,就是毫無意義的萬能架構!

研發工程師、架構師並不是作坊裏的熟練工,我們不能總是通過自己的感覺去工作(往往我們的視野是有限的,空間、時間限制了,一葉障目),有很多設計原則經過許多業內優秀的先賢驗證總結。這些都是我們做好工作必須去要學習的!

軟件架構定義

一個軟件系統抽象概況層面結構體集合(骨架),並且是創建這些結構和系統的約束規則

每個架構都由軟件元素集合(模塊
組件、連接器等)
、這些元素之間的關係,以及這二者各自的屬性 組成。

          

元素、關係、屬性

一般人會認爲,架構關注宏觀方面,比如模塊(元素)、模塊與模塊之間的連接方式(關係)。但實際上,並不僅僅如此

JavaBean的規範說明書就明確規範了命名,隱藏在其後的想法是利用反射 實例化JavaBean對象,並且通過反射那些遵守命名規範的方法,從而獲知JavaBean的屬性,進而調用其屬性保存數據。

軟件設計不過半個世紀,而建築設計已悠悠千載。建築設計中,只要直接影響建築的整體質量都屬於架構範疇,例如,建築的水密性,美觀,可施工性。

正如建築設計,只要細節關乎到系統的整體質量,它就應該屬於架構層面的內容。(屬性)

          

爲什麼合適的架構很重要?

無論設計與否,軟件系統總會有一個架構。最差也是大泥球(a big ball of mud)。

架構是系統的骨架,而又不僅僅是骨架

鷹之所以善飛,豹之所以善短跑,袋鼠之所以善跳,都得益於各自的骨架。而各種骨架之中並不存在最好的,只有滿足特點下的最合適的。

軟件架構也是如此。

然而軟件架構又不僅僅是系統的骨架,除了顯而易見的特點之外,上文也說了架構不好必要的細節,比如某些看不見的規約也很重要。例如,鎖策略,內存管理,或者集成擴展方式。

架構影響質量

軟件系統能做什麼,這是軟件系統的功能。但是同樣託貨物,狗和馬都能做到,但是從效率等方面來看,馬肯定更勝一籌,也更持久可靠。

軟件系統也是如此,滿足功能性的同時,需要滿足一些性能屬性,如,安全性,可用性,伸縮性等等。

這就需要軟件架構的設計,比如一個面向100人的b2c網站,如果要面向1億人,那麼不變更軟件架構是無法滿足的!所以選擇一個合適的架構可以讓軟件系統事倍功半,否則就會事半功倍。

架構與系統功能互相獨立(基本上)

通常情況下,架構與系統功能是相互獨立的,相似的架構可以用在截然不同的功能上,功能相似的系統也可能採用截然不同的架構。

但重要的是,合適的架構可以與系統功能取長補短,互相混合。就比如我們理論上可以把船塢建在沙漠裏,但是很明顯加載水域附近更方便輪船下水。

架構約束程序

任何系統都有約束,比如目前做的訂單升級不得不考慮MQ不穩定的情況;再比如與不穩定的第三方進行同步交互,必須考慮第三方長時間異常,需要熔斷的約束。

約束的目的是保證系統具備特定的質量屬性!
系統不做什麼與系統做什麼同樣重要,比如安全相關的系統不會與不可信的第三方進行數據通信等。爲了保證特定的質量屬性(如安全性,可用性)就必須施加約束。就比如高鐵總是要跑在軌道上的,顯然不靈活,但是更重要的是保證了其應有的高速運行與安全性。
提升整體度,統一一致的設計比散落在各處的奇思妙想更合適。

架構設計的目標

軟件架構的終極目標是,對這個系統的維護與構建做到最大化的高效率
以下數據摘自《clean architecture》數據來源一個某公司的核心繫統。

以下圖表示了該系統發佈0~8個版本過程中,研發人員的增長。
在這裏插入圖片描述

以下圖表示了該系統發佈0~8個版本過程中,代碼數量的增長(KLOC表示千行代碼)。
在這裏插入圖片描述
以下圖表示了該系統發佈0~8個版本過程中,每行代碼變更的成本(fuction(人力\時間))
在這裏插入圖片描述
以下圖表示了該系統發佈0~8個版本過程中,人均生產效率的變化
在這裏插入圖片描述
以下圖表示了該系統發佈0~8個版本過程中,資金成本的消耗
在這裏插入圖片描述

問題在哪裏?

龜兔賽跑的故事很好的說明了這個問題,先跑的快並不能贏得勝利。
想要跑的快,先要跑的穩。

核心問題在於,兔子偷懶了:持續低估了那些好的、優良設計、整潔高質量代碼的重要性。

兔子總是會用一句話欺騙自己:“我們可以未來重構代碼,先上線最重要!”但問題在於,每次都是一樣的情況,每次過後都沒有人再會提及質量問題的隱患,大泥球就是這樣一步步堆積起來的!

分治、知識、抽象

分治

  • (如何分?)分割後的各個部分需要足夠小,以便一個人單槍匹馬的就可以解決。
  • (如何合?)必須考慮如何將分割後的各個部分組合、裝配爲整體

知識

開發者利用之前自己或別人積累的知識,解決各個部分內的問題,解決各個部門組合的問題。這些知識的積澱,考驗的也是最基礎的技能深度(個人認爲能夠做到源碼級、原理級就夠了)。

抽象

精簡問題本身,簡化問題複雜度。因爲抽象能力能夠解決軟件的複雜度以及規模的增長。抽象概念化,意味着更多遵循約束,而非隨意實現代碼就行(如下文的“job”,冪等算是基礎約束)。開發者主動對系統增加約束,可以增強他們的推導能力。

一個真實的示例

來看一個真實的示例,簡單來說這個系統的功能就是需要做數據ETL(Extract Transform and Load)的功能。

  • 階段一
    集中彙總收集所有數據,並使用grep等腳本命令進行查詢。
    隨着規模增大(熵增),查詢的開銷越來越大,而且技術支持人員無法正確使用相關命令腳本

  • 階段二
    所有數據會直接錄入關係型數據庫中,中央數據庫管理並CRUD,技術支持等非技術人員通過web界面進行查詢。
    隨着規模增大(1TB/month),查詢開銷越來越大,對硬件的使用達到瓶頸,需要更多的數據庫以及帶來的各種問題。

  • 階段三
    使用HDFS存在,並且對每種查詢進行分佈式MapReduce計算,然後保存索引結果。如果需要擴展,一般通過先再編寫一個job,來完成對結果的索引存儲。
    每個job每個10分鐘執行一次,大約5分鐘執行完成。因此最新結果會滯後15分鐘。技術支持人員依舊在web頁面查詢結果。

以上示例可以看到:

1.
階段1中,階段2中,可以隨用戶查詢條件的變化,實時查詢。而階段3中需要編寫新的程序滿足新的查詢,並且延遲15分鐘。階段1中,階段2中,可以隨用戶查詢條件的變化,實時查詢。而階段3中需要編寫新的程序滿足新的查詢,並且延遲15分鐘。

功能雖然相同,但是質量屬性(擴展性、延遲性、伸縮性)卻不同。
伸縮性(彈性):階段3>階段2>階段1
實時性(延遲性):階段1>階段2>階段3
擴展性 : 階段1>階段2>階段3

質量屬性是隨着複雜度相互抵消的,如果要一種屬性發揮到極致,其他屬性必然會收到消減!

2.
架構與功能完全獨立。不同的底層架構,實現了相同的功能。

視角轉換

在Edsger Dijkstra 1968年寫了那篇著名的文章<GOTO是有害的>,大量的駁斥者洶涌而出,但今天來看結論似乎無需爭議。

【<GOTO是有害的>:主要觀點就是論證goto這種模式的命令會導致大型問題無法拆分、無法獨立、不可測試等缺陷。
而作者認爲並推薦採用do-while,if then else ,until等語句可以構造出任何程序】

在每一輪新的抽象觀念的誕生,總會有迷戀的守舊者,只知道抱殘守缺,卻不懂兼容幷包、與時俱進。他們的目光只頂着新抽象帶來的缺陷,卻忽略其中陌生的優勢。
同樣適用於今日,如果不主動的思考架構,就會依舊停留在 一堆麪條代碼的大泥球之中a big ball of mud)。

過度設計與風險驅動

首先無論是敏捷設計或是傳統的瀑布流,設計與計劃肯定是需要的。但是對於沒有任何風險的、不會越來越複雜的、並不需要長期迭代的軟件,任何形式的過度設計都是沒有必要的!殺雞焉用牛刀。

但是對於長期迭代並且複雜化不斷增長的項目,前期合理的設計是保證系統遠離大泥球的保證。

因爲對於每個複雜系統,重構都是破費周章且有風險的。

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