Go 工程化 -一- 架構整潔之道閱讀筆記

前言

這說明什麼呢,說明了可能我們以爲過時的,古老的技術或者解決方案也是有用的

第一部分 概述

第 1 章 設計與架構究竟是什麼

軟件的架構的終極目標,以及如何衡量一個架構的優劣,尤其是兩個錯誤的觀點非常感同身受,我也說過類似的話語,還有一句話是 “當前的需求非常緊急,這只是一個臨時的系統很快就會被替換掉,我們先完成它”。作爲一個專業的技術人員我們需要有一些底線來保證我們的代碼架構和質量,不能輕易妥協,這在 Bob 大叔整潔系列的另外一本書中也有提到。

第 2 章 兩個價值緯度

我們當前處在公共技術的部門,這也是一個經常困擾的一個例子,所有的業務方在提需求的時候都會表示需求非常緊急,但是這個功能的實現對我們來說重要嗎?這個需要打上一個大大的問號,其他部門的同學其實是無法對評估需求對於我們的重要性的,這個需要我們自己來權衡。

第二部分 從基礎構件開始:編程範式

編程範式指的是程序的編寫模式,與具體的編程語言關係相對較小。這些範式會告訴你應該在什麼時候採用什麼樣的代碼結構 當前的三種編程範式,結構化編程,面向對象,函數式編程

第 3 章 編程範式總覽

第 4 章 結構化編程

第 5 章 面向對象編程

在剛學習編程的時候,學到面向對象一定會說到,封裝、繼承、和多態,但是通過這一章我們可以發現,面嚮對象語言的封裝不一定比面向過程的 C 語言做的更好,這裏強調的更重要的是使用多態的手段對源碼的依賴關係進行控制,主要是指通過接口來實現依賴反轉,這樣就可以將組件進行分離,可以進行獨立開發和部署。我現在主要使用的語言是 Go,有一個常見的問題就是 Go 是不是一個面嚮對象語言,回答也是 Yes or no,是也不是,Go 不支持繼承,也不支持函數重載,運算符重載等在面嚮對象語言非常常見的特性,但是 Go 的接口非常強大,不需要顯示依賴接口的設計讓我們在依賴反轉的使用上更加遊刃有餘。

第 6 章 函數式編程

在我們剛剛結束的上一個系列,Go 併發編程 中,我們講到的大量手段來避免數據競爭,這些都是由於在併發時寫入導致的,而函數式編程最重要的一個特性就是變量不可變,由於變量無法被修改所以自然而然就不存在數據競爭,也就不需要加鎖,這樣可以獲得很高的性能。

第三部分 設計原則

軟件構建中層結構的主要目標:

在之前的 《Go 設計模式》 系列文章當中也有提到 SOLID 原則,換個角度可以發現這些其實都是殊途同歸的一些東西,SOLID 原則的歷史已經非常悠久了,但是直到現在它仍然非常具有指導意義。

第 7 章 SRP:單一職責原則

第 8 章 OCP:開閉原則

開閉原則在架構設計上非常常見,其中最常見的做法就是使用接口實現依賴反轉,如果開閉原則實現的不好就有可能導致我們在進行後續功能擴展的時候牽一髮而動全身,成本非常的高。

第 9 章 LSP:里氏替換原則

第 10 章 ISP:接口隔離原則

由於 Go 接口的隱式依賴的特性,讓 ISP 在 Go 中處處可見,我們常常採用的方式就是在調用者處依賴接口,而不管實現,這樣就可以做到,模塊分離以及最小化依賴。

第 11 章 DIP:依賴反轉原則

第四部分 組件構建原則

第 12 章 組件

第 13 章 組件聚合

看到這三個原則會感到有點熟悉,像共同閉包原則就和 SOLID 中的單一職責原則類似,共同複用原則和接口隔離原則看上去也有那麼幾分相似,這些知識從不同的角度看待總結問題的不同術語。最後這個組件張力圖很有意思,這說明我們在進行架構設計的時候是不可能做到每一項都很完美的,這當中會有一個取捨的過程,書中講到,一般而言會項目初期會從三角右側開始,進行一段時間後會滑動到左邊,是因爲在初期爲了效率我們可以犧牲一定的複用性,但是隨着依賴關係越來越複雜,那麼我們就要考慮複用和擴展了。

第 14 章 組件耦合

在 Go 中在編譯器上就限制了我們不能出現循環依賴,所以我們大量的使用了 DIP 的方式,但是講層次拔高一點,從微服務的角度來講仍然不應該出現循環依賴,如果出現那麼在版本發佈的時候可能會導致災難性的後果,架構的原則都是想通的,我們要時刻警惕循環依賴的出現,對於微服務來說可以在 api 網關進行判定是否成環

第五部分 軟件架構

第 15 章 什麼是軟件架構

這個也是常常會遇到的問題,就現在我能觀察到的爲例,架構師級別的基本上沒有看到過再做一線的程序開發工作,僅僅是平時的各種管理,規劃上的事務就已經忙的不可開交,這其實不僅僅導致了架構師本身會脫節,同時也會導致下面的同學很少有機會學習到架構師們過往的經驗。

這一點其實很容易被忽略掉,因爲我們經常做的工作就是細節性的工作,在進行設計的時候很容易就不自覺的假定 Web UI,MySQL 數據庫這些技術選型,在這本書的最後一個章節還會講到,這些細節。

第 16 章 獨立性

第 17 章 劃分邊界

第 18 章 邊界剖析

不同的邊界的跨邊界調用的成本是不同的,對於服務而言跨服務調用的成本非常高,這樣我們在進行服務劃分的時候一定要儘量的內聚減少頻繁調用的情況。

第 19 章 策略與層次

第 20 章 業務邏輯

再次強調了不要偷懶,今天剛好看到之前寫的一個反面例子的代碼,代碼裏面有一個 GetA 函數,從數據庫當中獲取 A 對象數據和一些統計數據,這個函數中的統計數據部分其實只有在一個 Web 頁面的接口中使用到,但是爲了偷懶,在其他地方查詢的時候也調用了這個函數,導致最後很多地方的接口性能都由於這個沒用的統計數據多耗費了將近 1s 的時間。

第 21 章 尖叫的軟件架構

用例是架構設計當中最應該關注的部分,框架數據庫 Web 服務的選擇都是細節,這些細節應該延後選擇,我們的用例不應該依賴這些細節,這樣才能很好的測試

第 22 章 整潔架構

看過前面的部分再來看整潔架構這一章節會發現非常的自然

第 23 章 展示器和謙卑對象

這裏主要是將很難進行單元測試的行爲和容易測試的行爲進行分離,很難被測試的行爲常常會被分離成爲一個謙卑對象,這個對象非常的簡單,不會包含很多邏輯

第 24 章 不完全邊界

第 25 章 層次與邊界

不要過度優化,但是也不要什麼都不管的一把梭,架構師需要演進和取捨的,沒有完美的架構只有不斷持續演進優化的架構。

第 26 章 Main 組件

main 是一個程序的入口,這是最細節的部分,因爲之前爲了很多東西不被依賴,我們一般會採用接口來實現依賴反轉,這時候就會導致我們所有的依賴關係的構建都需要在 main 中進行完成,所以一般而言我們會在 main 中引入依賴注入框架。

第 27 章 服務:宏觀與微觀

雖然現在微服務架構非常火熱,基本上所有的服務都是拆分了服務,但是拆分了服務並不一定表示就解耦合了,也並不一定就真的能獨立部署,想一想這是現在很常見的,一個應用必須要和另外一個應用一同上線,根本做不了獨立部署。

第 28 章 測試邊界

不變的組件不要依賴多變的東西,這樣會導致非常難以測試

第 29 章 整潔的嵌入式架構

軟件並不會隨着時間磨損但是硬件是會過時的,而且換的還非常頻繁,這時候我們就必須要把硬件以及固件代碼給隔離起來,對了不要認爲我們不做嵌入式開發平時就很少接觸到這個,SQL 語句其實也是一種固件代碼

第六部門 實現細節

第 30 章 數據庫只是實現細節

數據很重要,但是數據庫系統是一個細節,書上這一章用了一個例子說明有時候可能真的用不到數據庫。換個常見的例子,我們可能系統剛開始的時候使用 SQlite 就可以,隨着業務發展用上了 MySQL,然後隨着併發的提高又會引入緩存組件,這些變化其實和業務邏輯都沒有關係,數據庫的變化是不應該影響到業務邏輯的

第 31 章 Web 是實現細節

第 32 章 應用程序框架是實現細節

框架的選擇要慎重,我們業務邏輯本身不能依賴框架

第 33 章 案例分析:視頻銷售網站

這一步看起來簡單,但是非常考驗一個人的功力

第 34 章 拾遺

這一章對比了四種架構風格,同時提出了,架構設計是需要考慮實現細節的,設計需要映射到代碼結構和代碼樹上,這個其實和最開始的 “軟件架構師自身需要是程序員,並且必須一直堅持做一線程序員” 交相呼應。如果可以在編譯時解決的問題,就不要放到運行時,編譯的問題往往要比運行時的問題好解決,這也是爲什麼 Go 的依賴注入框架我更加推薦 wire 的原因,同理作者提出了 如果要防止直接中 web 控制器調用數據層,那麼我們就不應該將數據層(repo)暴露出來,只需要暴露 usecase 就好了。

總結

之前其實也大概瞭解過整潔架構,從最開始覺得它又臭又長,到現在工作兩三年後覺得 “不聽老人言,喫虧在眼前”,當我們在對一個架構或者是事務進行批判的時候一定要了解它面對的場景以及它的理念,這是最重要的。當然軟件領域是沒有銀彈的,我們需要做的是吸收每一種思想,在不同的場景下做不同的取捨,接下來會有幾篇文章結合毛老師課上講的 Go 工程化相關的內容,以及我在工作當中進行的一些總結最後提出一種當下我覺得的 Go 項目的組織方式,這種方式不是最好的,但是我覺得是現階段最適合的。推薦大家在仔細的閱讀一下本書,期望你能有更多的收穫。

參考文獻

  1. 架構整潔之道 - 羅伯特 ·C· 馬丁 - 微信讀書
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/XwrLHuWyo0lypT1NGaRh7A