不畫一張架構圖講透架構思維

圖片

圖片

👉目錄

1?架構的定義

2?架構是為了解無解的問題-分工

3?抱殘守缺的好架構應該是怎樣的

4?適可而止的設計、恰如其分的架構與成敗論英雄

本文深入探討軟件架構的本質與設計方法論,從架構定義演變到現代架構實踐挑戰,系統分析架構設計面臨的業務復雜度、分工困境和現實約束。作者提出“架構即軟件設計本身”的核心觀點,強調架構應平衡業務需求、團隊協同與系統演進,并最終以實用主義視角提出“恰如其分的架構”理念。

關注騰訊云開發者,一手技術干貨提前解鎖👇

鵝廠程序員面對面直播繼續,每周將邀請鵝廠明星技術大咖講解?AI 時代下的“程序員護城河”。更有蛇年公仔等精美周邊等你來拿,記得提前預約直播~👇

01

架構的定義

架構是一個界定不清的東西,我們很難講清楚哪些東西是架構,哪些東西不是架構。但軟件行業里其實人人都在搞架構,軟件設計就是架構本身

架構這個詞出現得很早,有些人認為是 NASA(也可能是NATO) 發明的。最早的架構定義就是描述軟件的結構而已,但現在已經沒有多少人談論他們定義的“軟件架構”了。工程師很難以克制描述復雜結構的原始沖動,但描述復雜結構的普世標準并不存在。大家常見的各種定義,翻來覆去地重新講著“軟件架構是軟件結構的頂層設計或者抽象設計”之類的話。

即使是這種軟件架構的定義,也并不為所有人都接受。汗牛充棟的架構書籍里有各種各樣的觀點,有的進一步把軟件架構視作一堆組件和交互的設計,有的則把軟件架構視作架構師主觀意圖的體現。把自己當作架構師的人們,著迷于把軟件里的“不變與抽象的部分”和“易變與具體的部分”分離出來,把前者當作架構。

架構師們是如此地熱衷于做這樣一件事,以至于有些人認為架構設計好了就解決了基本問題,設計不好通常是因為架構不好。于是很多人開始刻舟求劍:從某某顆粒度開始的設計應該叫概要設計,從某某顆粒度開始的設計應該叫詳細設計,尋求一個穩定、確切的合理軟件架構成為了設計一個軟件的先決條件。

為了防止架構出錯,架構師們的原始沖動會讓他們尋求疊床架屋的解,詳細地從問題域推導解空間,然后一層一層堆流程。架構的設計儼然和建筑學里面的藍圖(blueprint)一樣,需要一開始就設計得嚴絲合縫。于是我們在軟件工程課本的早期歷史部分能見到了瀑布流方法論的失敗、OS 360制造了人月神話(Dijkstra 把蘇聯決定建造 OS360 兼容機稱作“美國在冷戰中取得的最大勝利”)。硅谷無數的“死亡行軍”、“夢斷代碼”項目為這種流程制造反動,又產生了敏捷運動,SOA 的顆粒度又被進一步縮小,產生了 MSA。如果你在上世紀七十年代參與一項軟件工程的架構設計,你要寫的架構文檔可能長達50頁,包含各種角度的架構描述;如果你在上世紀九十年代參與這樣的項目,你要寫的文檔可能會縮減至20頁(這要部分感謝軟件分工的進步,操作系統、工具鏈、中間件和業務邏輯終于被分離開來);如果你在當代參與一個中國互聯網公司電商系統的項目設計,可能有的架構師會按照4+1視圖構建一些架構基線,有的架構師會寫一些兼容 TOGAF 的視圖,有的架構師干脆只提供矩形、棱形組成的圖(沒錯,它應該是一幅流程圖。但大部分人并不遵守 UML 里流程圖的規范來畫圖)。在不同的架構師的視角里,架構到底應該詳細到什么程度、應該包含什么要素是無標準可循的

這就產生了一個問題:如果我們不能搞清楚到底什么是架構而什么不是架構,我們只能把“如何評價一個架構的好壞”這個問題,轉換為“如何評價一個系統的好壞”。我們無法區分系統中宏觀和微觀的優缺點,那么,“一個系統的架構會決定基本面貌的好壞”這個假定聽起來就不那么準確了。雖然它仍然符合人類的直覺,但我們無法決定,什么時候應該改進我們的“架構設計”,什么時候只是改進我們的某些“非架構設計”。除非我們肯承認,我們應該改變的不是架構設計,而是設計本身

只要稍加觀察,所有人都會同意,人類天然就有架構能力。人類在現實世界里理解各種各樣的架構,也創建各種各樣的架構。在軟件領域之外,人類設計了多樣的復雜結構,用來表達世界的復雜,也用來消解管理世界的復雜的心智負擔。我們有房子的架構,有組織的架構,有業務的架構,也有軟件的架構。所有工程師在第一堂軟件工程課里都在不斷學習:如何控制復雜度。

老師們會不厭其煩地教大家,如何拆類,如何拆子類,如何拆函數,如何拆子函數,又如何把這些程序組件連起來,成為一個可以運行的程序。不管這個程序的組件叫模塊還是叫例程,不管這個軟件的結構是不是丑陋的,它總歸是有結構的、且合乎一定道理的。這說明人類有一種把復雜的東西拆成 segmentation 的天然能力和欲望。人總要把用各種各樣的載體把問題裝進一個封閉空間里,然后面向封閉空間求解,導出問題空間的數與形,然后映射成解空間的要素,再組合得到一個心安理得的解。這種 segmentation 的能力就是架構的能力本身,每個人天生就有這些能力,每個工程師在初級工作里也大量使用這種能力。從這個角度看,架構能力既非架構師的專利, 架構也就不只是是架構師的造物,架構并非單單是抽象設計,架構就是軟件設計本身。當然,這個觀點并不新鮮,也并不是我發明的。我最近一次聽到這種觀點,是在21年 QCon 大會上陶文的演講里。

從外部視角來看,還有其他的定義可用,這些定義更加不拘泥傳統的標準。近些年像《整潔架構》、《軟件方法》之類的書里就引入了一些從結果定義架構的思路:軟件架構是對業務用例的回應,軟件架構通過對軟件復雜度的控制,為利益相關者(stakeholder)負責

02

架構是為了解無解的問題-分工

架構如何回應業務用例?如何為利益相關者負責?

所有的架構都是為了解決分工問題。正如組織架構是為了解決不同職能的人的分工問題一樣,業務架構是為了不同職能的業務模塊和流程的分工問題,技術架構是為了解決技術組件的分工問題。這種分工對架構有決定性的的影響,絕大部分的技術架構都無法逃脫康威定律的支配。商業組織的軟件開發者大多對這種現象司空見慣。在商業定律的支配之下,軟件開發只是生產要素起作用的一個基本環節而已。軟件架構對業務用例的回應方式是:讓各種業務用例用恰當的模塊分工落地。軟件架構對利益相關者負責的方式是:讓利益相關者在付出最小的使用和維護成本的前提下,得到最高的生產力,創造最多的核心價值

分工是很難解的永恒難題,實際上并無真正的可行解。因為復雜和混亂是無法徹底消除的,而且任何組織自身的發展過程里,總不可避免出現低熵體逐漸演化為高熵體的情況。王興有一個很有意思的洞察,商業和戰爭很像,一方面講創造,一方面講競爭。可以說,這種戰爭是永無止境的,創造是永無止境的,熵增也就是永無止境的。兵法里講,兵無常勢、水無常形。永遠會有新的業務模式出現,也永遠會新的組織模式出現,商業組織里很難有穩定的架構,激烈變動的組織里連相對穩定的架構都很難尋求-很多研發者也可以把這句話解讀為,活是永遠干不完的。這產生了這樣的常態:再好的架構,也難以避免兩類問題。從一方面看,因為資源的原因,任何系統一開始就不足以讓人心滿意足,軟件架構師們總是想著一個 System V1 還有種種缺點,不停歇地構思出一個 System V2,最后失敗(這類故事在《人月神話》很多);從另一方面看,任何系統永遠要防止從一個“還行的系統”跌落到“屎山”的狀態,架構師在戰火連天的商業周期里提心吊膽,生怕復雜度失去控制。架構如兵法,以正合,以奇勝。

大部分互聯網公司的新工程師,都會指責老架構不堪大用。他們轉化為老工程師以后,又會感慨老架構牽一發動全身:很多問題不上稱只有二兩重,上了稱一千斤都打不住。因為商業戰爭的快速變化,在一套系統里,各種野生架構和推定架構(這個詞我們下面會再次談到)并存,不同技術生代的造物被各種飛線黑魔法連在一起。有些工程師看著后端架構里,Velocity 寫的頁面和 React 寫的頁面混搭,就好像看著石器時代的尖矛利盾和火箭時代的火控雷達,被火車的掛鉤連起來一樣。這時候有些心存幻想的工程師也許會無能狂怒:到底是不是存在我還沒有學會的架構理念來處理這個問題,是不是當時我理解這個問題不夠深刻?

后面他們會有心無力地發現:沒有銀彈。架構是不能完全解決分工問題的,且它仍然要在這個支配性的枷鎖下實現一個個業務用例,它仍然要為利益相關者負責。在此前提下,我們應該接受一個事實:架構應該是一個抱殘守缺的東西,我們只能在不好里尋找好。ycombinator 社區里有條評論很應景:“There’s no silver bullet, only trade offs.I see this a lot and it bugs me, because it implies that it’s all zero sum and there’s nothing that’s ever unconditionally better or worse than anything else. ”

03

抱殘守缺的好架構應該是怎樣的

???3.1?如何應對業務復雜度

???3.1.1?軟件是用來解決現實問題的,所以軟件首先是對現實的抽象刻畫

在數學里,解是輸入的映射:y = f(x)。y 是 x 的產物,y 不同于 x,重點是找到 f。

在工程領域里所有的解都存在于解空間之中,而解空間又必須對問題空間負責,所以是從問題空間導出的。現實空間中有非常非常多的要素,要素之間有各種各樣的關系和生命周期。哪些是應該進入我們的解空間的要素,哪些要素應該經過轉化和重新抽象,哪些要素是我們憑空創造的(例如,現實中并不存在時間與事件,我們如何在一個系統里表達時間與事件?),這都是需要我們深入思考的問題。有些架構師們喜歡把思考這種過程的能力稱作解構和抽象的能力。一個架構要能夠滿足業務用例的訴求,其基石就是解空間里從現實空間中正確刻畫出來的元素和交互。《人月神話》把軟件的復雜度分為必然復雜度和偶然復雜度。作者認為,基本要素的復雜度是不受工程師控制的,也就無法減少,而把它們組合起來的系統的復雜度卻受很多組合方法的影響,是受偶然因素影響的。對問題基本要素不恰當的理解,很可能造成不恰當的必然復雜度,也就不可能得到多么低的偶然復雜度。

這個觀點看起來是容易理解的,但工程實踐里卻很難被做到。大部分的業務系統的構建者往往遠離第一線的現實場景,通過產品經理來理解現實問題。而對技術團隊的現實問題輸入,卻又凌亂且無序,甚至很多時候并不完整。快速迭代的商業節奏和流水一樣的職場更替,讓業務研發團隊對問題空間的理解很容易變得支離破碎。到底領域里有幾個邊界、幾個模型,每個模型的主子關系如何,模型有多少種狀態,狀態的躍遷關系應該是怎樣的,一起決定了一套系統設計的方方面面。這絕不是工程師能夠拍腦袋決定的(但是現實中經工程師卻經常被迫做“那我來拍一個”式的決定,仿佛設計一個系統好像買一個西瓜一樣簡單),必須認真聽取利益相關者相關的輸入。如果只是得到一些一鱗半爪的輸入,架構就無法貼合業務的發展,在走形中腐化,在腐化中走形。

可能有鑒于此,這些年來軟件開發領域里,領域驅動設計的思潮開始流行起來。大家終于逐漸認識到了領域知識才是設計的驅動方,這個主次關系不可調轉。要獲得對真正的復雜業務的認知,要尊重領域專家的意見。很多時候因為種種資源的原因,一個技術團隊找不到領域專家,只有請類似領域的領域專家來主導業務設計。譬如請電商的產品經理來當金融的產品經理,把金融票證的設計當成電商商品來處理,只要業務的場景稍加變化,整套架構就會顯得非常生搬硬套,令維護者如鯁在喉。有時候連類似領域的領域專家都找不到,架構者只好自己冒充領域專家,自己同時扮演主次關系的雙方閉門造車,架構的好壞聽天。模型的錯誤、交互的錯誤、關系的錯誤都是只重視框架而不重視細節的架構設計常犯的錯誤,而且很容易在項目后期導致項目失敗(這再次說明了系統設計里任何細節都可能是架構設計的一部分)。兔子有四條腿,桌子也有四條腿,如果構建一個買賣兔子的系統,能否滿足制造桌子的需求呢?很多架構上似是而非的錯誤,需要到很晚的時期才能被發現,而且絕沒有后悔藥可吃。對一個領域有完整的認知或者有構建這種認知的能力,是成為一個領域里合格的架構師的先決條件。一線研發團隊成員平時工作節奏非常緊張,學習領域知識被看做是一件膚淺的臟活累活,很多人都沒有辦法構建這種完整認知,于是很多系統的架構設計總是受一種超現實的引力場影響,逐步脫離現實。

抽象刻畫還有另一層的引申含義:進行最基礎設計(比如建模)時,我們就必須對軟件設計作出取舍(而非等到我們的架構需要變動的時候),軟件設計并非對于問題的直觀理解的全盤照搬。軟件對于業務而言是效率工具,它的構建和維護過程也必須擁有足夠好的效率,所以工程師天然有傾向于尋找簡化設計的動機。任何事越輕、越快、越敏捷,資源的投放和回報的速率也越來快。但不同的工程師對問題的抽象思路是不一樣的,很多工程師厭棄課本上的教學思路,也沒有認真學習過正統的 OO 思想,最后各師各法。這種實踐產生的大部分的面向對象程序設計只做完了 OO 思想的最后一環,即 OOP,工程師們只學會了如何“把行為包裝進結構體”里,并沒有學會 OOP 之前的 OOD,也沒有學會 OOD 之前的 OOA。這也是有些人不會畫也不喜歡畫 UML 圖、架構圖的深層原因,他們對設計的理解像沒有章法的素描,只懂得使用一些框與線。

各師各法容易犯的錯誤是,在“舍”的問題上比較隨意,而在“取”的問題舉棋不定。

面對一個復雜的領域,有的人只能抽象出五六個模型,在一個流程里對模型進行管理,其他的東西只能通過擴展屬性和猴子補丁把系統綁成一個大泥球;有的人能夠抽象出25個模型,其中有5個是聚合根,進而推導出若干個領域和限界上下文。

如果對比來看,后一個設計“提取到了更多的東西”,而前一個設計“丟掉了更多的東西”。前一個設計之所以“顯得不完備”,是因為建模者沒有完備的推理方法,在庖丁解牛的過程中拋棄了太多血肉經絡,只剩下一些“大而硬”的骨頭。舍棄過多的信息量而只是簡單地搭建起一些“大骨頭”的系統缺乏法度,業務一旦進入領域的深度區間,工程師應對復雜性的問題就苦于沒有趁手的武器(因為原本應該出現在此處的解空間正交元素,沒有被前面的設計制造出來),通常會用非常復雜的局部方案做急就章的設計。

如果項目進度緊張,加入更多的程序員趕工,就更容易出現《人月神話》里提到的“加入更多的工程師只會讓延期的項目更加延期”的悖論。有的架構師們開始意識到,需要一整套完整理論來專門解決工程設計的取舍問題,于是《分析模式》、《四色建模法》、《實現模式》、《企業應用架構模式》等書籍或理論相繼問世,專門探討“如何抽象”、“如何分類”、“如何取舍”,甚或“基于此的架構風格”。領域驅動設計還專門把這些設計活動冠以戰略設計和戰術設計這樣正兒八經的名字。第一次見到四色建模法或者領域驅動設計的工程師往往會大吃一驚,原來在做設計的時候別人會考慮那么多東西,而且這些方法論各成一派,讓人眼花繚亂。

大部分人學習這些“取法”時吃足苦頭。《領域驅動設計》成書于2003年,它在工程師群體里的接受歷史長達20年。簡化它的理解成本的紅色《實現領域驅動設計》遲至2013年才出版,作者還嫌沒有把問題講明白,在2016年又寫了一本藍色的《領域驅動設計精粹》。盡管全世界有幾十萬人前赴后繼地學習這項理論,領域驅動設計仍長期被當作一個空有定義、缺乏參考實現的口號運動。一直到2022年的今天,還有大量的團隊聲稱自己在“理解和實踐領域驅動設計”,各辟蹊徑地設計各種系統架構,最后交付的結果大相徑庭,恐怕 Eric Evans 本人都分不清哪個方案更正宗。

過高的學習門檻讓這些工作能力難以成為人人掌握的基本功,也并不容易達成共識,于是架構師順理成章成了刻界于碑的法度制定者。一個團隊的工程活動變成了我們最常看見的樣子:架構師負責搭搭架子,因為他能看到“別人無法看見的東西”,其他人負責接架構師拆解出來的工作任務。各出機杼的設計只能存在某些實現層面,在問題的整體看法上大家只能按部就班地相信架構師的專業判斷。如果領域驅動設計有段位的話,有些架構師對它的理解只停留在二段水平,有些架構師對它的理解已經達到四段水平,這種視角的高低差異是學院派架構和野生架構出現的深層原因之一。

架構設計的“取法”也并不是越高大全越好,我們并不能斷定“后一個設計”就是一個好方案。現代的架構或建模理論濃墨重彩地講它們的觀點時,用到的例子往往是年深日久的傳統領域里的項目,這些項目的業務流程經過長期固化,已經較易于“套上標準方法論”。而很多新興領域的業務流程和模型尚未定型,架構師并無多少成法可參考。有些架構師看待一個新興領域或者新興業務時就好像看到一段骨架,其很多細節并未顯化,無法想象這個骨架演化下去最終會成為一頭鯨魚還是一只獅子。架構師學習一個領域也需要認知迭代的過程(如前所述),而設計不等人,有時候他們需要在認知迭代的早期就做技術決策。

此時任何人做很詳細的設計都可能誤入歧途,業務的迅速的發展也可能把今日的模型拋棄。能看見“別人看不見的東西”架構師并不擁有無限視野和對未來的占卜能力,要在過度設計的誘惑和設計不足的猶豫之間徘徊,好像棋手面對一個棋盤難以落子。所以謹慎的架構師都能寧可留有余地而不做難以轉圜的決策,保留決策上的雙向門可進可退而絕不走進單向門。這時候有些聰明人采用這樣的方法:把先驗的事情和后驗的事情區分開來,凡是先驗的東西應該積極落地,后驗的應該努力探索,不斷讓團隊在奔跑中用高投入產出比的事情把架構上長長的留白填下去。

當然,哪些是先驗的東西,哪些是后驗的東西,絕不是隨便說說就能決定的。解決這個問題比較好的方法是尋找別人的經驗進行學習,是為對標(benchmark),為人后而不為人先。比較有意思的是,這種學習既要認真學習成功的經驗,也要學習失敗的經驗。成功的經驗里固然有能把路走出來的東西,失敗的經驗里也隱藏了非常多“想不到我也有翻車的一天”的錯誤。比如,馬老師看到 Supercell 的架構經驗,會得出“我要建中臺”的結論;而很多工程師見到阿里建中臺的架構經驗,會得出“我不要建中臺”的結論。當然,也有些師心自用的工程師會得出“不管三七二十一,老子就是要建中臺”的結論。

???3.1.2?軟件架構要適應不同協同者的差異

架構是要解決分工的問題的,我們首先要解決不同的工程師的分工問題。

有些工程師是架構師,有些工程師不是架構師,有些工程師是前端工程師,有些工程師是測試工程師,有些工程師是高級工程師,有些工程師是初級工程師,如何設計一個架構,讓這些人成為一個團隊,舒適地協同工作呢?

這就要求系統本身有多維度的結構。一個系統應該由若干條橫線和豎線切分成不同的局部結構。

首先,割線要把領域割開。現代的商業軟件仍然是疊床架屋構建起來的,大家談起一套軟件使用的技術的時候,往往會談到技術棧這樣一種層次結構。技術棧的邊界就代表了專業領域的邊界,以前需要一個工程師來完成的復雜軟件,現在可能需要 IaaS 層、PaaS、Saas層、存儲層、后端業務服務、接入層、客戶端協同構建。有的復雜系統甚至要把前臺和中臺割開,把業務的前后工序也視為不同的領域。復雜的系統,除了要做好專業技能的上的水平分層,還要按照模型的維護職責,劃定系統間的邊界(這就是現在領域驅動設計里拳不離手的領域劃分)。這樣分割終于可以讓各類工作者的工作專門化,也讓很多問題的解在不同程度上逼近了必然復雜度,儼然就是很多架構師夢寐以求的“本質解法”。

一個不恰當的架構里,很可能出現不恰當的切割線,使得不同工作者權責利糾纏在一起,讓一方對另一方的地盤指手畫腳,產生令人討厭的藕斷絲連。不恰當的邊界難以避免,領域驅動設計都無法徹底解決。通常,一個完整的架構域經過長期演化,能夠保證百分之七十的邊界劃分清楚,就已經非常不容易。在解這個問題的時候,有些架構師能夠妥善應用 MECE 原則,按照金字塔原理對架構進行拆分,設計出來的系統像穩定的大廈或者一棵倒置的大樹,就算相當不錯。

其次,割線要把不同熟練程度的工程師能夠工作的領域區分開。很多公司都對工程師的專業熟練度有不同的劃分方法,一個工程師進入一間公司,負責的 scope 可能是一個模塊,可能是一個系統,也可能跨越多個甚至多套系統。不同熟練程度的工程師能夠在單位工作時間里駕馭的復雜度是有差別的,于是“怎樣給他們分配合適的工作”,也成為了架構設計必須考慮的問題。

有鑒于此,以前的復雜系統架構非常注重模塊級設計(這個考量在某些日企的外包軟件設計里體現得尤為明顯),當代的架構師在設計復雜系統架構時,干脆變得非常熱衷于拆系統。如果把架構的演進和組織的生長進行類比,一個技術團隊的微服務變得越來越多,工程師也會越來越多。越來越多的微服務變得需要而且可以被普通工程師維護,越來越多的普通工程師也越來需要足夠簡單和獨立的 scope 以專注工作。有時候有些團隊的員工能力參差不齊,切線的設計還不能比照其他經典架構(按照其他經典架構的設計原則,水平分層的切線必須比照關注點的分離(separation of concern),最好按模塊的類型切分),需要針對實際情況量身定制,那更考驗架構設計因地制宜的能力。

當然,這些切割線的出現也沒有統一標準,需要考慮現實團隊的實際情況加以論證。正如上面我們談到的,分工問題過于復雜,所以現在過少和過多的系統拆分都可能走火入魔。過少的大服務對于大團隊而言是個擁擠的集市,不能支撐敏捷開發,過多的小服務在降低局部復雜度的管理難度的同時,又讓全域復雜度呈指數級上升,將很多模塊間交互問題轉化為系統間交互問題,需要引入許多分布式場景的穩定性保障措施(按照《演進式架構》的觀點,被拆分的服務可以被稱為架構量子,架構量子的數量超過可以被手工管理的數量的時候,微小的服務顆粒度就突然變得成本高昂而好處寥寥)。

這些年在各種技術大會里,形形色色的分享者們熱衷于談論自己為團隊設計了一個多么大的系統,或者多少個五花八門的微服務,卻不能講清楚這樣的架構演進到底給分工和生產力提升帶來怎樣的幫助,可見架構設計之難。探討這個問題探討得比較深刻的是《The Art of Scalability》(中文名《架構即未來》)這本書, AKF 立方體框架把大部分的理論和實踐都包羅進去,逐漸有了越來越多的擁護者。

比較容易被忽視的是,我們還要解決工程師和非工程師的溝通問題。

現如今的互聯網產品經理都很熟知福特宣傳汽車的例子:當客戶認為自己需要一匹更快的馬的時候,福特告訴他其實他需要的是更快的速度,而不是一匹在世界上不存在的馬,然后福特把汽車給了自己的客戶,要他學會對產品的思維轉變。這個例子很好地說明了解決方案制造者要面對的困難:自己實際產出的解決方案可能和最初客戶要求的解決方案不是同一個東西,但本質上又必須是同一個東西。軟件是為軟件工程師所制造,而又要服務于所有 stakeholder 的多維復合體,每個人都覺得自己有必要在一個項目里講兩句。比較理想的狀況是:所有人在軟件設計溝通的時候使用同樣的語言,能夠保證大家始終探討的是同一個問題,這也要求所有人對產品的組件、功能有清晰的認知,沒有歧義。有的人能夠接觸到的軟件是實際運行的產品,有的人能夠接觸到的軟件是一些產品描述文檔,也有些人能夠接觸到的軟件是真正運行的代碼。不同角色有其自身視角出發的觀察方式,也有其獨特的描述語言。

不做架構師不頭痛的一個問題是:軟件工程師和非工程師對“所見與所得”這兩個問題常常得出相左的意見,甚至引發曠日持久的爭吵。我們都知道,只有使用了編譯器才能讓程序員寫出合乎語法、邏輯正確的程序,只有這樣的程序才能在處理器上正確運行。但世界上并不存在從客戶需求生成產品經理文檔的編譯器,也不存在從產品經理文檔生成軟件設計的編譯器。每個人都在關注產品本身,但每個人眼中同一個軟件的樣子卻不一樣,所以有些項目以如下的死亡螺旋無可挽回地走向失敗:一開始產品的一些概念沒有界定清楚,而軟件研發過程中的建模、設計、項目跟蹤又產生了各種各樣的信息偏差,研發周期里出現一些蒙眼狂奔的流程,然后產出一個很難用但又很勉強能用的東西。這個東西看起來好像最初客戶和產品經理想要的那樣,但又有一些與預期不一致的地方。大家好像知道為什么這個東西難用,但又很難講清楚難用到底是為什么。

軟件設計是很難向非程序員講清楚的。一個系統里運行的模塊通常是用數學思維加上拗口的語言寫的,程序員非經過訓練不能看懂。一個大型程序如果沒有好的架構,只要經過很多代程序員的維護,就會變得好像地質巖層一樣斑駁復雜,經過訓練也很難讀懂,把它講明白需要拿出地質考古的毅力來。試圖給其他人講明白這個程序是怎么運作的,哪里可以改動,哪里不可以改動,哪里可以這么改動,哪里不可以這么改動,都需要很多“老鳥”的口傳心授。但這種碎片溝通一般不能把系統的全貌描述清楚,也不能講明白系統的發展脈絡,通過這種語言描述一個系統就好像管中窺豹,難免有局限性。而產品經理要了解這個系統的實際情況,經過這種“以其昏昏使人昭昭”的轉述,自然也會得到有偏差的信息。另一方面,程序的遺留設計(legacy design)和產品經理一開始的預期就可能不一致,但為了給產品經理交差,很多程序員也會表面上答應了產品經理的直接需求,實際上做出了另一個方案,通過某種程度上的“轉譯”來間接達成產品經理的預期。能夠巧妙地彌合預期產出和實際產出的 gap 未必是壞事,但“很難向其他stakeholder講清楚這個軟件”很容易導致熵增難以收拾,最后所有的需求都很難做下去,產品和研發的矛盾也就開始了。

想要讓“所見與所得”相匹配,并不是要求程序員對其他 stakeholder 的要求照單全收,但一定要有辦法讓程序的邏輯以一種外行人也能看懂的方式呈現出來。這就要求這個軟件設計不僅是合理的,而且還是可以被簡單解讀的。我們人類其實早就發現了解決這類問題的解法:復雜的結構應該是支持透視的,透視能夠讓我們看明白復雜結構的好壞。很多人很早就搞明白了語言是線性的而不適合描述非線性結構這一弊端,提倡“用圖表說話”。軟件工程領域的大師們也很早就發現了 diagram 的重要,而且他們發現軟件不能使用單一視角描述,軟件是橫看成嶺側成峰的,于是 UML 在一開始就有 9 種視圖。當然 UML 一開始也是為了程序員設計而不是專門為其他 stakeholder 而設計的,但這類圖表是比較易于為其他人理解的已經為工程實踐所證實。要讓一個設計能夠在多種視角和語言之間反復切換而不產生歧義,使用統一的圖表語言已經成為很多產研團隊實踐中的共識。如果一個軟件團隊使用“4+1”視圖構建架構基線,其中的用例和場景腳本描述往往必不可少;而有些團隊使用 TOGAF 之類的架構描述語言描述自己架構基線的時候,在技術架構之外還會加上業務架構的圖表,來讓大家對系統的功能有一些非線性的認知。

這些圖表必須駕馭整個設計,對所有圖表的閱覽者而言,這些圖表就是“所見即所得”,它的存在讓產研工作易于溝通。只要上了圖表,架構上的錯誤和丑陋就無所遁形,這又會倒逼架構設計者不斷維護這個架構的功能合理性,努力在設想和實現之間保持微妙平衡。而好的設計也會讓其他 stakeholder 清楚明白這個系統的每個用例的流程是怎樣的,每個用例涉及的模型有哪些,它在生命周期是怎樣變化的,不會出現“福特的客戶竟是我自己”的情況。學會讀懂一些基本的架構圖和畫一些流程圖,甚至已經成為了一些產品經理的必備技巧。 到這里可能有些讀者會意識到一個問題:看起來軟件大部分時間是寫給計算機程序運行的,但軟件設計大部分時間是寫給人看的。做好的設計不僅要有對計算機編程的思維,還要有對人編程的思維。一個很爛的程序的背后,不僅有很爛的圖表表達,也有一團亂麻的思緒。

更容易被忽視的是,我們還要解決現在的程序員和未來的程序員的溝通問題。

很多程序員都承認一個事實:一個程序在最初的時候是最好寫的,隨著維護的深入,它會變得越來越難寫。一個程序員看到一段非常復雜的邏輯的時候,有時候難以理解為什么它要這樣拐彎抹角地解決一個特定問題,有時候甚至連為什么存在這樣特定的問題都想不明白。有的程序員受限于自身水平,不知道怎么設計一個好的解決方案,于是寫下了一段蹩腳的代碼。也有的程序員接手一個救火項目,江心補漏無計可施,不得不寫下了兼容舊方案而又支持新場景的補丁。還有的程序員因為一些外界的軟硬件的“硬因素”限制,不得不解決一些“看不見的問題”,很多年后這些“硬因素”已經消失,解決方案仍然還在。這些解法往往難以更改,讓人不喜歡卻無可奈何,而且它們絕不會隨著業務向好而變得越來越好。因為大部分人寫下它們的那一刻就已經決定欠下技術債務不還,它們只會安靜地躺在代碼倉庫里,等待有人發現它們奇奇怪怪的那一天到來。而且那一天到來的時候,人們會突然發現,原來壞的代碼會產生壞的代碼。也有一些代碼使用了很高超的編程技巧,優化了某一個特定的問題,卻因為種種原因和某個特定場景綁定在一起,等到維護者再次打開這段代碼的時候,場景已經發生變化,而這段復雜的解決方案卻無法被改動了,于是好代碼也會產生壞代碼。

程序員們很早就搞明白程序的 maintainability - 可維護性是一個問題。有些人把可維護性問題當作可讀性問題來看,可讀性問題的大致定義是:怎樣用最簡單的語言給未來的人講故事,讓你意圖能夠正確地傳達到后來者的思維里,他不再有你的信息量,卻仍然要在你的工作基礎上做決定。換言之,你能點亮無法與你直接交流的程序員頭腦里的燈嗎?

第一個常見的解法是教會所有程序員寫好注釋。StackOverflow 社區里有個著名的問題What is the best comment in source code you have ever encountered? [closed],歷數了程序員在寫不好程序的時候通過寫注釋來挽回顏面的種種手段。有些軟件工程理論認為好的代碼應該是自描述的(self-explanatory),所以代碼本身被稱作內文檔(inner document),如果代碼本身的表達能力不夠,才需要引入注釋。大部分人讀程序的時候也確實如此,我們只關注程序可運行的部分,對程序不可運行的部分興趣不大,所以大部分人不喜歡寫注釋,也寫不好注釋。很多時候大家遇到討厭的解決方案,與其寫下詳細的注釋來補完這個設計,不如寫個 TODO 安慰自己早晚有一天會回來修復這個問題。但大部分的 TODO 會變成永遠的 TODO,很多遺留代碼里的注釋甚至是被簡單拷貝復制粘貼過來的,只會誤導后續的維護者。指望程序員寫不好代碼的時候能寫好注釋通常會失敗,而且讓讀者痛苦的并不是注釋,是注釋也不能修復的設計問題本身。

第二個常見的解法是教會程序員重構。原始的重構教條涉及的都是小模塊的變更,關乎怎樣重新設計若干個對象之間的外觀和行為,隱含著對合理的原則和合理的框架的要求。也就是說,對于由表及里牽連甚廣的設計問題,重構并不適用。所以大部分人嘴上說要重構,實際上是在重寫。對于復雜架構的重寫也不是一件輕松的事情,以至于最近很多架構書籍專門發明了一個新詞“架構重構”(Architectural Refactoring)。合理的重構首先要考慮外部兼容性,被逼到窮途末路的程序員會采取激進的策略,經常把一切推倒重來,引發次生問題。一個可重構的程序一開始就應該是易于重構的,重構的問題又變成一個怎樣設計一個可以被重構的架構問題。

有人因此認為,程序的可維護性看起來是一個整潔代碼(clean code)問題,它背后還藏著一個整潔架構(clean architecture)問題。一個軟件要處理的領域的必然復雜度是一回事,它表征出來的復雜度是另一回事。在程序開發的古典時代,Unix 程序員是通過把難解的問題的高手解法封裝進框架代碼本身,只把可擴展部分暴露出來,來簡化降低編程的門檻。

使用 K&R 或者譚浩強的教材入門的程序員,在課堂上學到的一個程序是如何寫一個“hello world”。一個“hello world”看起來只寫了一行代碼,但底層依賴的操作系統和標準庫代碼可能高達兩萬行。寫一個 “hello world” 的可讀性很高,也很難出錯,所以很多人認為 Unix 是優秀架構的典范,Ken Thompson 和 Dennis Ritchie 是世界上最偉大的架構師,他們構建的 Unix 系統可以讓架構師和其他維護者跨越時空對話,彼此永遠也不認識。類似的例子我們也可以在當代的軟件行業里見到很多,Spring、RESTful API 規范和各種 ORM 框架的出現讓受困于 EJB 的程序員一下子變得很容易開發出易于維護的企業級程序,因為大部分的規范已經被框架制定好了。如何設計 bean,如何使用 ioc,如何設計切面,一個 API 應該叫什么名字,一個 DAO 應該怎么設計,都有約定俗成的范式可尋。一代程序員處理基本問題的思路可以被下一代程序員快速地理解,大家都覺得 convention over configuration 的時代來臨了。寫出很爛的代碼的影響很小,因為每段代碼都被框架壓在一個小范圍里。

可能很多人都沒有意識到,正是因為這個時代有那么多現成的 CRUD 架構和規范可以采用,才產生了那么多的 CRUD 高薪崗位。但還是有很多業務的問題過于復雜, CRUD BOY 們無法簡單處理,如果架構師本人還是編程高手,還會繼續使用把“能力內建進架構的通用部分”的思路來追求架構的整潔,力圖把“平臺的代碼和業務的代碼分離開來”。食髓知味的程序員會在通用框架的基礎上追求業務框架,追求“通用能力的沉淀”和“軟件要素的復用”,于是產生了 COLA、SOFA等業務腳手架框架,也產生了各種業務中臺、技術中臺。這一代程序員遇到一個問題不要緊,我接下來會想辦法把它的解決方案封裝起來,下一代程序員將只知道這個問題被解決了,而不必關心這個問題是怎么被解決的。架構通過自凈化來抵御熵增,把復雜度裝在水平線下,受益于此,大部分程序員終其一生都不需要讀 Spring 和 JRE 的源碼。

???3.1.3?架構是用例的回答,所以架構要包容所有相關用例

我們很難有窮舉完我們的業務的用例的時候,我們也不可能一次實現完所有用例,所以我們的架構必須有包容能力,能夠面向未來編程。

人類最初學習數學的人生體驗大同小異:我們可以用窮舉的方式來理解從一到十的加減法,因為我們的手指恰好就是一種可以適應這種窮舉模式的計算系統。但如果要設計一套計算器來容納所有的自然數的四則運算,我們就不可以繼續使用我們的手指系統,必須另起爐灶了。我們的祖先給出的第一個答案就是算盤。算盤是一種可以用演繹的方式來進行正整數加減法的工具,它用有限的必然復雜度,支持無限種組合模式,而且任意組合的偶然復雜度可控。但后來人們發現算盤的計算能力也有限,因為我們還需要處理非正整數的計算問題,我們相繼發明了了計算尺、手搖計算器、計算機和現代的 matlab(matlab是一個很神奇的東西,大部分的知乎用戶都認為中國人自己無法設計和實現這么復雜的軟件)。

計算系統的演化恰似我們系統架構的演化。任意一套系統的合格標準之一,就是它能夠解決特定領域內的所有問題,哪怕今天這個問題沒有出現,等它明日出現的時候,也可以被我們的系統解決。如果某個問題不能被我們的系統當場解決,它也應當被稍加修改的系統解決。

這種架構特性被很多架構理論稱為 extendability - 可擴展性。它們把尋求 extendability 定義為“怎樣使用最小的維護成本來新增所需功能”的問題。一個可擴展性好的架構,好像一個有無數倉位的架子,當出現新的業務需求的時候,架構師只要把業務拆解好,讓自己或其他工程師把業務放進指定倉位即可,如果一類業務不能放在原本尺寸的倉位里,這個架子也有制造無限多的新倉位的可擴展空間,新舊倉位無耦合,獨立運作與發展。業務之間擁有同質性,這就要求架構能夠適應這種同質性。譬如一套電商系統今天能夠支持蘋果的交易,明天也應該能夠支撐西瓜的交易,如果之前蘋果無需退貨,而西瓜需要退貨,架構也應當滿足這種業務訴求。某些中臺架構師把業務裝入架構的行為稱作“一個業務使用了一項商業能力/平臺產品”,把新增一類功能叫作“新增一項商業能力/平臺產品”。如果一個架構已經設計并實現了所有的的商業能力或者平臺產品,可以被看作“能力封頂”。

可擴展性要求我們的系統有擴展點,簡單的可擴展性需求要求我們有簡單的擴展點,復雜的可擴展性需求要求我們有層次化的擴展點,甚至還需要與之配套的配置系統。現代的軟件工具設計者們,很早就意識到程序的可擴展性的問題。面向對象程序設計里控制復雜度的方式是,通過允許繼承,讓程序組件表現出多態的行為。這種設計模式讓一個架構多出了無數擴展點,所以面向對象程序設計在誕生之日起就大行其道。一個復雜的系統如果如上述的架構思路設計為一個樹形結構,則從樹的葉子節點向上,每一層都是可擴展的。架構師可以在對解空間認知迭代后,通過擴展某一個層次來實現帶有新必然復雜度的原子能力,也可以通過配置化的系統,實現對原子能力的組合,依靠有限的偶然復雜度交付完整的系統流程,這也就解決了“怎樣使用最小的維護成本來新增所需功能”這個問題。

很多架構師在尋求可擴展性的道路上探索得非常遠,從最初的 eclipse 的插件化架構出發,找到了不同的落地路徑。阿里出現了 TMF 和星環,美團也出現了 BPF。現在各種中臺/平臺化架構,已經可以在分布式場景下,讓一個系統將另一個系統視為擴展點甚至插件,通過全域的編排能力來交付復雜流程。這既滿足了之前提到過的合理的邊界拆分的前提,也保證了系統總能包含相關用例。這種架構的思路,往往是全域復雜度堆積到一定程度的推導重來的結果。要找到合適的擴展點,需要對業務核心能力非常強的洞察,懂得在大尺度上做正交分解和組合,無法實現能力和接口的標準化,也就無法實現可編排的架構,稍有不慎就會設計出大而無當的系統。

理想是美好的,現實是骨感的。也有很多的系統架構,總是受到業務劇烈變動的沖擊,這產生了這個時代特有的“外部適應性”與長期主義的辯證關系。

如果把互聯網公司的歷史寫成一本書,讀者翻到第10頁的時候,很難想象第100頁寫的是什么。一家公司10年前可能在做團購,10年后可能就在做無人機了。這些年流行的“互聯網思維”,一夜之間突然想把所有的事情都拿互聯網公司的做法重新做一遍。業務沒有邊界,意味著有些領域要處于長期拓荒狀態,有些領域又要經常和其他領域聯通。在這種高度不確定性的背景下,很多業務的規劃和策略變來變去,讓系統設計置身于高度不確定性的選擇之中。大家習以為常把“擁抱變化”掛在嘴邊,每個團隊每天前進三十公里,但事后看地圖,發現行進的路線好像一條無法直線入海的河流,充滿了幾字形的反復。一個團隊抱著試錯的心態去接觸某一個新業務,突然發現自己試對了,趕緊在某個領域的建設里加大投入,但過幾年發現又試錯了,于是又要調轉船頭,轉而建設其他東西,留下前面的能力半途而廢。

打個小鎮做題家式的比方:一個高中課程所有的知識點總共有1000分,高考使用100分鐘的考試來隨機出題考其中的100分的知識點,這時候一個人能夠拿多少分取決于他把1000分的知識點學了多少,這100分鐘的考試時間里,考卷上的100分和自己學會的知識點的交集又是多少。小鎮做題家解決這類問題的思路是,努力苦學,爭取把1000分的知識點學會。但做業務系統沒有這種預設的封閉與公平:一個團隊的組織力大概只能做出300分的題目,業務的考卷可能有10000分的題量(這10000分就是我們需要做的選擇:在一個近乎沒有邊界限制的市場空間里,我們要做哪些業務,這些業務需要多少種能力幫助落地,我們如何建設這些能力),答題時間也長達1年,這個團隊如何拿到100分?如果還有其他競爭對手,各自的組織能力不僅分數不一樣,而且偏重也不一樣,本團隊將如何亂中取勝?一種解決問題的思路,是像小鎮做題家做更多的題目一樣,繼續努力苦練基本功,讓自己的組織力從300分往10000分逼近;另一種思路是把考卷剪枝,讓10000分的考卷往300分的能力逼近。采取前一種策略吃力不討好,很多團隊會采取后一種策略(在產品層面還有專門的方法論,比如 pmf 和 stp),但這種選擇成功率不是很高,大部分的時候容易選錯;也有很多時候市場的年景發生特別的變化,答卷里分值最高的知識點恰好就是本團隊不具備的,本團隊非把自己300分的組織能力提升,以適應外部變化不可。出現很多的摩擦和拉扯不可避免,原本需要長期可用的能力的價值因此無法自證,“架構方法論”就變成了一種佯謬,因為資源的投入拿不到結果。

不變的東西是事實上存在的,只不過不存在本本主義里而已,亞馬遜就提倡把戰略設定在恒定不變的東西上。也就是說,10000分的考卷里,有一些知識點是必考的知識點。按照某些方法論的觀點,這些東西是某個市場的核心能力,或者關鍵輸入指標的控制能力,這些能力就是握住未來的所有用例的線索。但找出這些不變的能力是很難的,需要反復的認知迭代,而且會經歷很多進道若退的曲折。做架構師要有對領域發展的長期視野和專業判斷,專業判斷不簡單迷信方法論,依賴于實事求是的分析和 trial and error(到底這個能力是否真的可以對業務如此重要,需要確實的業務結果支撐,而且也需要其他stakeholder最終買單),而且在遇到幾字形的反復的時候,要有非常強的定力,在反復中也仍然堅持建設可擴展的架構,努力適應短周期的變化,把變化的東西在長周期裝進不變的架構里,達成適應外部和長期獲勝的平衡。

???3.2 軟件架構還有很多意想不到的問題要考慮

很多架構師認為,業務復雜度之外還應該考慮技術復雜度。還有非常多的東西,是技術復雜度這一范疇不能簡單概括的,我們姑且把它們先稱為“其他維度屬性”。“其他維度屬性”又多又亂,有些維度隱藏得很深,讓人意想不到。

很多 system design 面試題,都會讓設計者“設計一個淘寶”或者設計一個“twitter”。這些系統要實現的功能非常復雜,這要求架構者天然擁有上文提到的應對業務復雜度的能力,除此之外這些面試題里還隱藏有其他陷阱:架構者如何識別這個場景下的性能瓶頸和隱式約束。架構設計中的性能瓶頸已經涉及非常多的細節,隱式約束更是難以捉摸。

譬如,一個推特系統每日產生若干張圖片,理論上產生的圖片數會超越某些存儲方案的極限值,你會如何處理這種問題?如果引入某個存儲方案,如何在解決它的可伸縮性問題的基礎上,解決數據可靠性問題?如果你選擇了某種共識算法,它將在什么時間界限內滿足哪個級別的一致性?你的方案達成一致是同步還是異步的?你將如何權衡讀一致性和寫一致性的成本?

譬如,一個推特系統要設計一個開放 API,如何保證它的安全性?你將使用多強的加解密算法或散列算法?你如何權衡某些密碼學問題的加密強度和資源密集型應用之間的關系?你的 API 如何恰當地在區分了資源的邊界,支持各種各樣的其他系統通過多種協議接入?

譬如,這個系統的可管理性如何?可觀測性如何?如何衡量它的可演化性,你是否有意識地使用健康度函數和引導變更機制來持續演進它?

譬如,你所在的團隊使用了某個細分行業專有的存儲解決方案,但此時此刻有一種上云的方案可以減輕你的成本,你是否愿意舍棄某些特性,轉而上云?或者反過來,如果有必要,你是否會離開公有云,尋找自己的 ldc 的解決方案?如果你所在國的政府禁止你使用某種解決方案,你將如何切換你的系統架構到替代方案上-在切換之前,你的系統是可移植的嗎?

譬如,今日你設計的架構只能給出某些核心能力,但公司的長期戰略是要在若干年后達成若干目標,你的架構設計是否能夠孵化出達成這些目的的核心能力?你將如何兼顧長期視角和短期的落地訴求,既不放過眼前的業務機會,也能夠讓架構演進終達到目標?

通行的架構理論都認為架構應該滿足“可用性”、“可靠性”、“可測性”、“高性能”、“一致性”等通用特性,除此之外,還有若干個諸如“管理性”、“可復用性”、“可測試性”之類非常難以理解的質量屬性,讓人讀得暈頭轉向。現實之中做架構決策時要考慮和處理的“xxx性”更是多得讓人抓耳撓腮。我們很難講一個使用了 Oracle 的某類特性而固若金湯的銀行系統不是一個好架構,我們也很難講一個去掉了 Oracle 從而每年節省了若干成本的架構不是一個好架構。而要深刻理解某類架構的優劣,需要架構設計者有深厚的理論基礎支撐,也要有非常多的架構實踐經驗。這些經驗和支撐既需要長久的學習,也需要大量的實戰經驗。從這個視角來看,架構既是學問,又是實踐。

04

適可而止的設計、恰如其分的架構與成敗論英雄

到此為止,我們發現架構設計不是在解決一個問題,而是在解決若干個打包在一起的相關問題的集合體。書上的架構理論并不能解決我們的全部問題(亞馬遜的工程師幾曾感慨,幾年的 SOA 改造經驗讓他們學到了在書本上永遠學不到的東西)。因為資源有限,我們很多時候不能推導出全部問題。即使推導出了全部問題,我們也未必能夠推導出全部答案。即使推導出了全部的答案,我們也未必能夠把答案全部落地(有些答案甚至是相互矛盾的)。我們需要權衡的東西有十萬個為什么,問題永遠答不完。

于是乎,現實的世界里,人人都是架構師。學院派架構師設計學院派架構,野生架構師設計各種野生架構。每個團隊都有自己獨特的問題要解決,做架構設計的時候要相信自己實事求是的判斷,而不是盲從別人的架構理論和實踐。架構設計既涉及長期思考,又涉及短期思考,很容易套用 SWOT 的決策框架分析:為什么這個事情就應該由本團隊來做,我們有哪些機會和優勢值得讓我們能投入(SO戰略),我們有哪些劣勢和威脅逼迫我們不得不做這個事(WT戰略)。我們到底是要延伸我們某些能力的臂長,還是修補我們木桶的短板?這是擺在每個架構設計者桌面上的問題。很多公司每年都會做大量的業務戰略規劃,進而推導技術的滾動規劃,試圖平衡 SO 和 WT 兩類問題,但最后大家發現,很多時候能夠解決 SO 問題就相當不錯了,WT 類問題不到暴雷的邊緣不會有人收拾。

在朝生暮死的互聯網領域,很多技術架構的存活周期非常短,習慣了傳統的企業級 Java EE 架構的架構師看到某些 TO C 場景下的架構設計,甚至會有朝菌不知晦朔,蟪蛄不知春秋的感慨。一個電商團隊的架構,可能還沒有演進到能夠支撐百萬量級的用戶的階段,就因為業務被取消而被廢棄。在這種場景下追求高大全的架構原則的實踐,遠不如幫助企業抓住業務機會重要,怎樣讓架構適應外部變化成為了唯一值得考慮的問題。這時候被團隊新成員罵的架構,只要能夠維持業務跑下去并最終走上正軌,搖身一變居然也成了一種好的架構了。

這些年新出版的架構理論,已經開始承認這樣的現實:“我們不能處理所有問題(更談不上拿到所有好處),只能應對我們需要迫切解決的風險,做一些適可而止的設計”,這種架構思路被稱為“恰如其分的架構”。這種理論認為,最初人們構建的系統,其實是使用通用架構設計出來的,是為“架構無關的設計”。再往后人們發現了很多特殊的核心能力或者質量屬性可以嵌入到設計里非業務的部分,才產生了“提升架構的設計”。架構范式會在這種流程里慢慢浮現出來,這時候某些早期架構決策變得非常重要了,才出現“專注于架構的設計”,進而出現“參考架構”和“推定架構”。參考架構是可能成為事實標準的架構,它嘗試對特定領域的架構方案給出一些固定解法,比如一個電商域的架構應當擁有營銷域;推定架構是已經成為事實標準的架構,比如一個電商域的架構肯定要有商品域和支付域。大量的架構演進實踐告訴我們,復雜而完整的大型架構并非不可達成的,但它們的落地也不能一蹴而就,需要投入很多資源,也要甘冒很多風險,經歷許多曲折。有些架構師在分享的時候取標題《如何在戰火中建造一座城市》,并非虛言。

我們應當警惕技術流行里的宏大敘事。很多“高大上”的架構理念一面世,立刻就引來一窩蜂的模仿者。仿佛舊的架構設計方法已經落伍,擁抱新的理念才能取得項目的成功,使用舊設計的架構終將失敗。這個行業前幾年動輒“使用中臺助力傳統企業軟件架構轉型”,過幾年又鬧著“要平臺化,追求可配置,學 TMF”,這幾年又開始大喊“中臺已死,DDD 才是未來趨勢”,渾然不顧 DDD 是一個早于中臺誕生的設計理念。這些理念彼此之間并無高低優劣之分,也絕不是什么魔法咒語。聲稱只要使用了某項技術,就能神奇地解決不可解決的問題,只是一種技術迷信。比所有哺乳動物強大得多的霸王龍滅亡在白堊紀,真正在歲月長河中幸存卻是弱小而靈活的哺乳動物。MVC 是一個已經誕生近40年的設計模式,現在仍然是很多企業應用架構的首選架構模式,并沒有完全被逐漸流行的 DDD 分層架構所取代。而曾經甚囂塵上的很多種 enterprise pattern,如今卻已銷聲匿跡。求全必毀,過猶不及,時間會證明哪些架構方法是合適的。

到此為止,本文的基本結論已經出來了。好的架構能夠讓各種業務用例用恰當的模塊分工落地,讓利益相關者在付出最小的使用和維護成本的前提下,得到最高的生產力,創造最多的核心價值。這需要架構的所有利益相關者(用戶、維護者)的一致認可,最后我們只能在力所能及的時間周期里,以成敗論英雄

-End-

原創作者|梁川

感謝你讀到這里,不如關注一下?👇

圖片

📢📢來領開發者專屬福利!點擊下方圖片直達👇

圖片

圖片

快速變化的業務中,架構設計該先保速度還是夯根基?歡迎評論留言補充。我們將選取1則優質的評論,送出騰訊云定制文件袋套裝1個(見下圖)。7月29日中午12點開獎。

圖片

圖片

圖片

圖片

圖片

圖片

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/92474.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/92474.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/92474.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

SpringCloudGateWay 使用nacos網關自動負載均衡

安裝好nacos后(參考以前文章SpringCloud 使用nacos注冊服務,使用openFeign調用服務-CSDN博客) 新建一個項目,添加 spring-cloud-starter-gateway-server-webmvc spring-cloud-loadbalancer spring-cloud-starter-alibaba-nacos-d…

Hiredis 構建 Redis 命令實戰指南

一、同步命令構造 1.1 redisCommand(fmt, …) 最常用的同步接口即 redisCommand,其原型如下: void *redisCommand(redisContext *c, const char *format, ...);參數 c:已連接的 redisContext*format:與 printf 類似的格式字符串//…

【數據庫】國產數據庫的新機遇:電科金倉以融合技術同步全球競爭

7月15日,國產數據庫廠商中電科金倉(北京)科技股份有限公司(以下簡稱“電科金倉”)在北京舉行了一場技術發布會,集中發布四款核心產品:AI時代的融合數據庫KES V9 2025、企業級統一管控平臺KEMCC、…

大模型 Function Call 的實現步驟及示例詳解

大模型 Function Call 的實現步驟及示例詳解一、Function Call的核心流程拆解二、結合代碼詳解Function Call實現步驟1:定義工具(對應代碼中的tools列表)步驟2:實現工具函數(對應代碼中的get_current_weather和get_cur…

Linux運維新手的修煉手扎之第24天

mysql服務1 mysql命令客戶端(mysql.cnf)命令 \c--ctrl c \s--顯示當前狀態 \r--客戶端重新連接 \h--查看幫助信息 exit退出客戶端 \G--垂直格式顯示查詢結果連接MySQL服務器--[rootrocky9 ~]# mysql(mysql -u用戶名 - p密碼 -h服務端ip -P服務端port -S服務端sock -e "my…

面向對象分析與設計40講(7)設計原則之合成復用原則

文章目錄 一、概念 二、示例(C++ 實現) 1. 違反合成復用原則的示例(過度使用繼承) 2. 遵循合成復用原則的示例(使用組合) 三、總結 1. 繼承是“強綁定”,組合是“弱關聯” 2. 繼承固化“靜態結構”,組合支持“動態變化” 3. 繼承放大“設計缺陷”,組合隔離“局部問題”…

Git 完全手冊:從入門到團隊協作實戰(4)

Hello大家好&#xff01;很高興我們又見面啦&#xff01;給生活添點passion&#xff0c;開始今天的編程之路&#xff01; 我的博客&#xff1a;<但凡. 我的專欄&#xff1a;《編程之路》、《數據結構與算法之美》、《C修煉之路》、《Linux修煉&#xff1a;終端之內 洞悉真理…

解決Spring事務中RPC調用無法回滾的問題

文章目錄問題分析解決方案實現原理解析執行流程說明運行實例正常流程執行執行異常流程關鍵優勢在分布式系統開發中&#xff0c;我們經常會遇到本地事務與遠程服務調用結合的場景。當本地事務包含RPC調用時&#xff0c;如果事務回滾&#xff0c;RPC調用已經執行就會導致數據不一…

sqli-labs通關筆記-第13關 POST報錯型注入(單引號括號閉合 手工注入+腳本注入兩種方法)

目錄 一、字符型注入 二、limit函數 三、GET方法與POST方法 四、源碼分析 1、代碼審計 2、SQL注入安全分析 3、報錯型注入與聯合注入 五、滲透實戰 1、進入靶場 2、注入點分析 &#xff08;1&#xff09;SQL語句 &#xff08;2&#xff09;admin) #注入探測 &…

康復器材動靜態性能測試臺:精準檢測,為康復器械安全保駕護航

在康復醫療領域&#xff0c;無論是輪椅、拐杖、假肢還是康復床&#xff0c;每一件器械的強度與穩定性都直接關系到使用者的安全與康復效果。如何確保這些器械在實際使用中經得起反復考驗&#xff1f;Delta德爾塔儀器推出的康復器材動靜態性能測試臺&#xff0c;憑借其高精度、智…

vue3中el-table表頭篩選

效果如下&#xff0c;可以勾選表頭進行隱藏&#xff0c;也可以對表頭進行拖動排序index主界面 <script> let tempHead []; const showFilter ref<boolean>(false); let tableHeadList ref<TableHeadItem[]>([{ prop: "displayId", label: "…

數據結構 之 【排序】(直接選擇排序、堆排序、冒泡排序)

目錄 1.直接選擇排序 1.1直接選擇排序的思想 1.2直接選擇排序的代碼邏輯 1.3完整排序代碼 1.3.1一次只選一個最值 1.3.2一次篩選出兩個最值 1.4直接選擇排序的時間復雜度與空間復雜度 2.堆排序 2.1堆排序的思想 2.2堆排序的具體步驟 2.3堆排序圖解 2.4完整排序代碼…

用手機當外掛-圖文并茂做報告紀要

前陣參加一個峰會,看到演講嘉賓每翻一頁PPT,下面的觀察就舉起手機一頓拍。實話說這種拍下來的,難說還會拿出來看,而且再看的時候也未必能對應到當時主講人的一些解釋 。 如果現場將圖片保存到筆記本電腦,并快速記錄關鍵信息,這樣聽完一個報告可能就直接輸出一篇報道了。 有…

Vue的ubus emit/on使用

這段代碼是 Vue.js 組件中的 mounted 生命周期鉤子函數&#xff0c;主要作用是監聽一個名為 “macSelectData” 的全局事件。具體行為如下&#xff1a;分步解釋&#xff1a;mounted() 生命周期鉤子 當組件被掛載到 DOM 后&#xff0c;Vue 會自動調用 mounted() 方法。這里常用于…

rsync報錯解決

問題說明 [rootlocalhost shyn]# rsync -avz --checksum "root192.168.159.133:/tmp/shyn" "/tmp /shyn"WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! …

ArKTS: DAL,Model,BLL,Interface,Factory using SQLite

HarmonyOS 用ohos.data.rdb 用DBHelper.ets 共用調用SQLite 庫&#xff0c;進行DAL,Model,BLL,Interface,Factory 框架模式&#xff0c;表為CREATE TABLE IF NOT EXISTS signInRecord ( id INTEGER PRIMARY KEY AUTOINCREMENT, employeeId TEXT NOT NULL, employeeName TEXT NO…

MySQL JSON 數據類型用法及與傳統JSON字符串的對比 JSON數據類型簡介

文章目錄前言1. 基本用法JSON數據類型 vs 傳統JSON字符串1. 存儲方式2. 查詢方式對比3. 索引支持JSON存儲對象和數組的性能考慮1. 存儲對象2. 存儲數組性能對比總結最佳實踐建議前言 MySQL從 5.7 版本開始引入了 JSON 數據類型&#xff0c;專門用于存儲 JSON 格式的數據。與傳…

C++:list(1)list的使用

list的使用一.list基本的結構1.環狀雙向鏈表2.哨兵節點3.迭代器4.節點結構5.鏈表遍歷6.迭代器失效二.list的基本使用1.test01函數&#xff1a;主要測試std::list的初始化方式及遍歷2.test02函數&#xff1a;主要測試std::list的常用成員函數操作3.測試結果如下三.list的其他操作…

ArcGIS地形起伏度計算

地形起伏度計算地形起伏度步驟1&#xff1a;計算最大值。步驟2&#xff1a;計算最小值。步驟3&#xff1a;計算地形起伏度。地形起伏度、地形粗糙度、地表切割深度和高程變異系數均為坡面復雜度因子&#xff0c;是一種宏觀的地形信息因子&#xff0c;反映的是較大的區域內地表坡…

llama factory新手初步運行完整版

1、新建conda環境名稱為llama_factory&#xff0c;并激活 conda create -n llama_factory python3.10 conda activate llama_factory2、激活后可檢查內部包是否純凈&#xff0c;要確保環境內包較純凈&#xff0c;不然后續安裝對應包會出現一系列水土不服的問題&#xff0c;導致…