☆【細品架構8/100】好代碼是架構的根基

本文主要是繼續研讀了資深架構師王概凱Kevin執筆的《架構漫談》系列的《架構漫談(八):從架構的角度看如何寫好代碼》的心得感受。王概凱Kevin結合自己多年的架構經驗,通過不同的視角,重新審視架構的本質,從而產生一力作《架構漫談》系列,作者希望能夠拋出自己從實踐中得出的一些觀點,并引發大家的一些思考,歡迎大家溝通討論。

如需要閱讀原文,請關注公眾號“聊聊架構”,從歷史文章中獲取《架構漫談》系列。

本文內容結構圖:

好代碼是架構的根基

當我們有了好的架構,那就需要考慮架構如何落地,而這時候代碼就顯得無比重要了!千萬不要讓代碼成為架構擴展的瓶頸。

在上篇文章中,我們得出一個結論,軟件架構實際上包括了:代碼架構,以及承載代碼運行的硬件部署架構。實際上,硬件部署架構最終還是由代碼的架構來決定。因為代碼架構不合理,是無法把一個運行單元分拆出多個來的,那么硬件架構能分拆的就非常的有限,整個系統最終很難長的更大。

經常會聽說,重寫代碼,推翻原有架構,重新設計等等說法,來說明架構的進化。這實際上就是當初為了完成任務,沒有充分思考所帶來的后果。這也并不是架構進化的事情,而是個人對問題領域的逐漸深入理解的過程

所以,本文進一步探討如何把架構的思考進行落地,細化到我們代碼的實踐當中,盡量不要讓代碼成為系統長大的瓶頸,降低架構分拆的成本。


在前面文章提到,軟件實際上是對現實生活的模擬,虛擬化。這是一個非常重要的前提,直接決定了我們的代碼應該分為幾部分。結合每個部署單元所承擔的責任,可以明確的拆分為兩個不同的責任:

  1. 表達業務邏輯的代碼。很多人把這部分叫做Domain Logic,或者叫Domain Model。這部分實際是來源于生活的,必須保持和現實生活中的切分一致,并非人為的抽象而成。

  2. 對用戶提供訪問并保存業務邏輯運行結果的代碼。計算機的狀態保存有一個缺陷,本機保留業務運行結果有很大的問題,一般都在外存儲設備上保存,也便于擴展。

所以單個部署單元的代碼可以分為兩個部分,如下圖所示:

單個部署單元代碼架構

從這個圖中可以看出,軟件代碼的相關利益人為運行時的訪問人員和存儲設備。而service的代碼是最復雜的,需要服務于三方,代碼人員的負擔是最重的。為了把這三方的變化對service的影響降到最低,對于service還必須進一步的分拆為三個部分,讓每一個部分都能夠獨立的變化,這樣這三方的變化就不會產生連鎖響應,降低成本。如下圖所示:

Services拆分代碼架構

這樣,就劃分成了幾個責任:

  1. Service就專注于user的需求,并組合Glue Code提供的服務完成需求。

  2. Glue Code專注于組合business的調用,管理Business里面對象的生命周期,并且通過Repository保存或加載Business的狀態

  3. Business專注于實現業務的核心模型

  4. Repository專注于數據的保存,并和存儲設備一一對應。

大家注意看,還是樹形架構。并且左側的主要需要計算機的相關理論知識,并且要直接面對用戶的需求。右側的更多的需要面對業務的核心。只要這幾塊的開發人員互相商量好了接口定義,這幾個部分的開發就可以并行的進行,極大的提升開發的效率,縮短開發的時間。要做好這幾部分,還需要注意,邏輯只允許存在于Business中,Service、Glue Code、Repository都不允許存在業務邏輯。為什么呢?首先我們來看看什么叫業務邏輯。


什么叫業務邏輯

首先這個定義的前提是指軟件代碼中的邏輯,不是現實生活中的邏輯。在軟件代碼中,不需縮進和計算的順序調用,包括縮進的代碼目的是cache exception的,都不算邏輯,除此以外都是邏輯。以下用嚴格的順序調用來指代這種代碼。因為順序調用是計算機的特性,由編譯器來決定的,當然最本質的是因為我們計算的基礎都是圖靈機。在現實生活中,順序調用也是邏輯,大家不要和我們這里說的業務邏輯相混淆。

為什么說除了Business代碼中有邏輯以外,其他地方不能有邏輯呢? 我們每個部分分別分析:

  • 如果service里面不是嚴格的順序調用,有很多分支,那么說明這個service做了兩件或者兩件以上的事情。必須把這個service分拆,確保每個service只做一件事情。因為如果不這么分拆的話,一旦這個service中的某個部分發生變動,其他的部分的執行必定會受影響。而確定到底有哪些影響的溝通成本非常高,其他相關利益方沒有動力去配合,我們往往不會投入精力仔細評估。最后上線會出很多不可預料的問題,最終會導致損失用戶的利益,并且肯定會導致返工,損壞自己的利益。如果是有計算的邏輯的話,比如受益計算,訂單金額計算等,那么這部分應該是Business代碼需要完成的,不能交給service代碼來實現。

  • Glue Code里面如果不是嚴格的順序調用,同理會和service一樣遇到同樣的問題

  • Repository里面如果不是嚴格的順序調用,包括存儲訪問的代碼里面(比如SQL),會導致邏輯進入到存儲設備中。存儲設備的主要目的是拿來存儲的,一旦變成了邏輯計算的主體,就會導致存儲設備無法通過增加機器的方式橫向擴展長大。這個時候就沒有架構了,只能換性能更好的機器,這個叫scale up。只有scale out才能算架構。

以上都會導致架構無法快速的橫向擴展和分拆,并且增加了修改的成本,這些是不符合開發人員以及業務的利益的。


這么做的好處有哪些呢?

Service、Glue Code、Repository里面的代碼是嚴格的順序調用,那么這些代碼只要做連通性測試即可,不需要單元測試。因為這些代碼都需要和很多上下文打交道,很難做單元測試。這樣才算是真正的組合。

Business不訪問任何上下文,不訪問任何具體的設備,所以這部分代碼是非常容易些單元測試的,并且單元測試必須100%覆蓋。因為其他地方沒有業務邏輯,所以一旦有問題,就可以斷定是Model的問題,單元測試肯定可以發現。如果單元測試沒有發現問題,那么單元測試一定有問題。線上問題的模擬也就變得非常的簡單,單元測試也能夠得到進一步的補充。

Repository很容易按照存儲設備本身的最小訪問粒度來完成工作,比如DB,完全可以做到單表訪問。因為這個時候存儲設備只關心存取數據,完全和業務沒有關系。做表的分拆也是非常容易的事情,存儲設備通過增加機器就可以橫向擴展長大。很多人會擔心說,沒有了join,訪問DB的次數是不是更多了,會導致性能下降? 按照現在網絡的條件,網絡訪問和Disk IO訪問的差距已經不大了,合理的設計下,多訪問幾次DB并不會導致這個問題。另外如果多臺DB的話,還能通過并行加速訪問。

由于Service、Glue Code、Repository代碼簡單了,才可以讓我們的開發人員投入更多的時間研究業務,畢竟這部分才是軟件所真正服務的對象。

我們再來看一個實際的例子,如下圖所示:

實踐中代碼架構

Manager類實際就是Glue Code。有幾個注意點需要說明一下:

  1. 不能把Business Model當做數據對象來處理,Model關心的實際上是業務行為,數據只是是這些行為的結果。所以Glue Code需要把Model轉換為Entity,Entity和存儲設備里面的存儲粒度一一對應。比如在DB中,每個Entity對應一張表,并且跟著表的變化而變化,這樣就保證存儲的變更不會影響Model。同樣Service和用戶之間的數據交互,也是不會和Model之間相關的,確保用戶的需求變化,不會影響到Model。因為用戶的需求變化是最頻繁的,沒有邏輯,可以讓我快速的滿足業務的需求。
  1. 在Service這里,最好不要考慮代碼重用。因為當多個不同的角色訪問同一個接口,一旦某個角色的需求發生了變化,就會要求開發人員去修改。而這個修改往往會影響到其他的角色,需要這些角色一起配合來確定是否受影響,但是這些角色因為沒有需求,往往不會配合。這樣就給開發人員造成了很多不必要的溝通,成本是非常高的。最終都會導致線上Bug,影響最終的用戶。所以盡量給不同的角色不同的Service,避免重用,降低溝通成本。很多人會說這樣Service不就太多了嗎? 這樣Service注冊,查找等管理需求就出現了,Service治理中心就是來解決這個問題的。因為Service里面沒有邏輯,所以開發和管理非常的簡單,可以快速應對業務的變化。我們只有更快地變,更容易的變,才能更好地應對變。
  1. Business Model是必須要重用的,一旦發現重用出現問題,那么說明Business Model的識別出現了問題,這是一個我們要重新思考Model的信號。Business Model必須是一個完美的樹狀,如果不是,也說明Model的識別出了問題。

在實際操作中,Service、Glue Code、Repository不能有邏輯,實際上和很多人的觀念是沖突的,認為這個根本做不到。做到這一點需要很多的學習成本,但是一定可以做得到。當發現做不到的時候,可以斷定是業務的分析出了問題。比如不該合并的合并了,不該計算的計算了。這個問題一定有辦法解決的,做不到都是理由,無非是想早點把自己的工作結束罷了。雖然剛開始會比較困難,一旦把這個觀念變成自覺,開發的質量和效率馬上就能高好幾個級別。

我們真正想快速的完成代碼工作,就要克服自己對時間的恐懼,真正的去研究業務的問題,相關stakeholder的利益,把這個變成我們的習慣。寫代碼的時候讓該出現邏輯的地方出現邏輯,讓不該出現的地方不能出現。一旦不該出現的地方出現了邏輯,那么要馬上意識到,這個地方是一個坑,這個問題一定和業務的分析不透徹有關系。

以上只是針對單一的Service部署單元的分析,擴展開去,對于其他的部署單元也是類似的。每個單元的下一級都可以認為是Repository,每個單元的上一級都可以認為是User。這些實踐在項目中都有用到,非常的有效,迭代的速度非常的快。很多人擔心Business Model建不好,其實沒關系,剛開始可以粗糙一點,后續可以慢慢的完善。這個架構架構已經隔離好了每個部分的變化對其他部分的影響,變化成本都在可控的范圍之內。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,412評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,514評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,373評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,975評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,743評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,199評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,262評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,414評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,951評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,780評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,527評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,218評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,649評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,889評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,673評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,967評論 2 374

推薦閱讀更多精彩內容