.png)
在軟件開發領域,架構設計是確保系統高效、穩定運行的重要環節或者稱之為重要動作。無論架構從簡單到復雜,還是從復雜回歸簡潔的演變過程。在這個過程中,又無論是初創公司還是大型企業,架構提效始終是技術團隊的核心追求。本文將從穩定、性能、代碼三大維度出發,結合實戰經驗,探討如何有效提升架構效能。
當然架構也有自身的矛盾統壹,在架構提效上,系統的運行正常和問題頻出是壹對矛盾,功能的快和慢是壹對矛盾,工程的整潔有序和無序是壹對矛盾。這三對矛盾正是架構提效的矛盾。
如果不穩定,系統三天兩頭出故障,研發人員成了救火隊員,系統的效率將無從談起,穩定是我們談架構效率的基礎。如果性能不高,在網絡基礎環境穩定的情況下,訪問壹個頁面 3S 才響應,那我們也不好意思說架構有效率。如果代碼亂成壹鍋粥,比如大段大段面條式的代碼,再比如滿眼望去 N 多個 if 結構語句,研發人員加壹個功能都要查找好久,也是無顏談效率。
因此,我們認為,穩定、性能、代碼是架構提效矛盾中的主要方面。接下來我們將從這三個主要方面去介紹。
軟件工程發展了這麽多年,高可用、高擴展、高並發已經有大量的文章篇幅,從宏觀的角度去講如何做微服務、如何分庫分表,如何使用緩存等等。因此呢,本篇文章想聚焦到架構矛盾的微觀層面,也就是偏工程結構、偏代碼方面去闡述這三個要素。另外本篇文章的思想也參考了前輩們的研究成果,我也附在了文末。
“萬事萬物都是運動的,發展的”。業務功能變多,用戶數量變多,團隊規模變大。如果沒有規則和規範的引導和約束,系統逐漸野蠻生長,逐漸碎片化。那麽,我們的系統何談穩定呢。
我們就希望能找到這樣的壹種規則、規範 -- 正交分解或者叫做正交設計。
架構設計的過程就是壹個業務正交分解的過程。
架構設計並不僅僅是技術層面的規劃,更重要的是對業務邏輯的深入理解和把握。通過正交分解,我們可以將復雜的業務系統拆解成若幹個相互獨立但又彼此關聯的模塊或組件。這些模塊或組件在保持功能完整性的同時,還能實現高度的內聚和松散的耦合,從而提高系統的可擴展性、可維護性和可重用性。
正交分解的關鍵在於消除重復、分離關註點和管理依賴。通過這壹方法,我們可以將業務系統中的公共部分和可變部分進行明確的劃分,從而實現對業務邏輯的精準掌控。在架構設計過程中,我們需要不斷地對業務進行抽象和分解,直至得到壹系列規模可控、結構清晰的小模塊。這些小模塊通過組合和協作,能夠形成更加復雜且功能完善的軟件系統。
因此,正交分解的思想是我們架構設計保障穩定的重要方法基礎。
想快,就使用 “戰術設計”。曾經這是很多程序員的法寶,因為他們認為這樣開發 “確實快”。
大多數程序員以稱為戰術編程的心態來進行軟件開發。在戰術方法中,主要重點是使某些功能正常工作,例如新功能或錯誤修復。乍壹看,這似乎是完全合理的:還有什麽比編寫有效的代碼更重要的呢?但是,戰術編程幾乎不可能產生出良好的系統設計。
想快的 “戰術設計” 會造成常見的下面這樣的情況。
“團隊新人不熟悉系統,為了急於上線壹個特性,又不想影響到系統的其他部分,就會很自然地在某個地方加壹個 flag,然後在所有需要改動的地方加 if 判斷,而不是去調整系統設計以適應新的問題空間。”
在壹個充滿活力的軟件開發團隊中,新成員小張剛剛加入不久。他對於團隊正在使用的復雜系統還不是很熟悉,但面對緊迫的項目進度和上級施加的壓力,他急於證明自己,並希望能盡快為團隊做出貢獻。團隊正計劃上線壹個新的特性,這個特性需要在不幹擾系統其他部分的前提下實現。
小張在瀏覽了系統的代碼庫後,發現要全面理解並調整整個系統設計以適應新的特性,需要花費大量的時間和精力。他深知自己作為新人,在這方面還有所欠缺,因此,他決定采取壹個他認為更為 “高效” 的方法:在某個關鍵位置添加壹個臨時的標誌位(flag),然後在所有需要改動的地方都加上 if 判斷,以確保新特性能夠按時上線,同時盡量減少對現有系統的影響。
雖然這種方法在短時間內確實讓新特性得以順利上線,但團隊中的資深成員很快便發現了潛在的問題。這種做法雖然看似快速解決了問題,但實際上卻在系統中埋下了隱患。它不僅增加了代碼的復雜性,降低了代碼的可讀性和可維護性,還可能在未來引發更多的 bug 和性能問題。更重要的是,這種做法違背了軟件開發中的最佳實踐,即應通過優化系統設計來適應新的問題空間,而不是通過添加臨時性的補丁來解決問題。
“幾乎每個軟件開發組織都有至少壹個將戰術編程發揮到極致的開發人員:戰術龍卷風”,而且常常被視為團隊” 英雄 “,因為能 “快速完成任務且高產”。
“戰術龍卷風” 通常以戰術編程為主要手段,即采用最快速、最直接的方法來解決當前的問題,而不考慮長遠的影響和代碼的可持續性。這種方法在初次使用時往往能夠取得顯著的效果,任務完成得既快又好,贏得了團隊成員的贊譽和領導的認可。
然而,隨著時間的推移,“戰術龍卷風” 所留下的隱患逐漸暴露出來。由於缺乏對系統設計的深入理解和長遠規劃,他的代碼往往難以維護和擴展。當需要添加新功能或修復 bug 時,團隊成員往往需要花費更多的時間和精力來理解和修改他的代碼。因此,第二次和第三次修改時,效率會大幅下降,甚至可能引發新的問題。
實際造成結果:第壹次快、第二次慢、第三次更慢。
我們做業務開發,代碼的優雅簡潔,不能局限在壹段方法,還是要從整個工程結構然後在到類、到方法,這樣從宏觀到中觀再到微觀的整體去要求。我們的應用工程結構,常見大致分為四層。分別是 api 層、biz 層、domain 層和 dao 層。
這個時候我們就要很清晰地熟悉每壹層的職責,然後將我們的代碼放入進去。首先,api 層的作用,正如它的名字壹樣,是提供 api 服務的。向誰提供 api 呢,比如客戶端,比如 APP 端、pc 端等等,公司外面的客戶,比如 isv 等。其次,biz 層的作用,這壹層也叫業務服務層。它主要負責編排。把壹個業務場景下的主流程邏輯處理完成。這個主流程會涉及到多個原子接口,就在這層負責組裝。再次,domain 層的作用,也叫做領域服務層。按照 OO 思想,領域編程的思維,我們的” 厚對象 “的代碼都在這層。比如訂單域、運費域等。這裏對 “這壹層的位置” 多說幾句,在沒有形成領域之前,這層壹般叫 service 層,不過我們都是建議領域思維編寫代碼。最後是 dao 層,也就是我們的存儲層了,負責持久化。
在清晰了每壹層的作用之後,如果我們的代碼職責也是按照這樣逐層放入的,那麽大體是符合我們的整潔要求的。但是呢,隨著時間的推移,需求的增多和變化。原來整潔的工程結構和代碼已經不那麽優雅了。
這個時候,壹般會出現兩類現象,壹類是業務層(biz)變的臃腫,能力層(domain)變的單薄。另壹類是出現了網狀調用。而且這兩類現象也很有可能是混合在壹起出現。
這兩類現象會直接帶來下面 4 種結果。
1、biz 層越來越” 胖 “。胖了之後,還長成了兩小層。上小層是面向單壹業務場景的 “業務 biz 層”,下小層成了通用場景可復用的 “通用 biz 層”。
2、service 層越來越” 瘦 “。當 service 層變薄了以後,也就只能淪為 service 了,而這樣的 service 層跟 dao 層實際沒什麽區別,更不能再稱之為 domain 層或者沒有機會演變成 domain 層。
3、但是也不是所有的 service 層都變瘦、變薄了。可能有的萎縮,有的膨脹。人員與設計的差異,導致顆粒度不壹。
4、出現了網狀調用。原本 biz-1 -> service-1 的實現鏈路下,隨著新增業務邏輯,又新起了壹個 service-2,鏈路演變成了 biz-1 -> service-1-> service-2。“這樣的趨勢持續發展下去,會發現 biz-1 下的 service 調用鏈路越發的復雜,呈現為壹顆深度調用樹,而 biz 層失去了業務編排的作用退化為壹個業務場景入口的標誌符”。有可能後面繼續 3-4-5-6,越挖越深,不見盡頭。
很顯然,到這裏,這樣的結構現狀,代碼現狀,已經遠離了我們簡潔和優雅的初衷。也談不上提效了。
到此,我們介紹了架構提效中的穩定、性能和代碼這三個主要的方面,限於篇幅和架構本身的實踐性,還需要我們在架構提效上進行持續的優化。需要我們在穩定、性能、代碼三大維度上不斷探索和實踐。通過高可用架構設計、性能優化策略、模塊化與解耦、代碼質量與規範等措施,我們可以構建壹個既穩定又高效,且易於維護和擴展的系統。
“在復雜的事物發展過程中,有許多的矛盾存在,其中必有壹種是主要的矛盾,由於它的存在和發展規定和影響著其他矛盾的存在和發展。”
架構的發展本身也是對抗熵增這壹矛盾的過程,我們上面描述的穩定、性能和代碼中的矛盾方面有是圍繞和關聯這壹主要矛盾的。在這個過程中不僅有系統的有序變無序,也有組織的簡單變復雜。我們既要關註技術層面的提升,更要註重團隊協作、知識共享和持續改進的文化建設。只有這樣,我們才能在快速變化的市場環境中,保持競爭力,不斷前行。