在 Go 中我是如何組織包的

構建項目跟寫代碼一樣具有挑戰性。而且有很多種方法。使用錯誤的方法可能會讓人很痛苦,但若要重構則又會非常耗時。另外,要想在一開始就設計出完美的程序幾乎是不可能的。更重要的是,有些解決方法只適用於某特定大小的程序,但是程序的大小又是隨着時間變化和增長的。所以我們的軟件應該跟着出現過解決過的問題一起成長。

我主要從事微服務的開發,這種架構非常適合我。其他領域或其他基礎架構的項目可能需要不同的方法。請在下面的評論中告訴我您的設計和最有意義的地方。

包及其依賴

在開發微服務時,按組件拆分服務很有用。每個組件都應該是獨立的,理論上,如果需要,可以將其提取到外部服務。如何理解和實現呢?

假設我們有一個服務,它處理與訂單相關的所有事情,比如發送電子郵件的確認、將信息保存到數據庫、連接到支付提供商等。每個包都應該有一個名稱,該名稱清楚地說明了它的用途,並且遵守命名標準。

這只是我們有 3 個包的項目的一個例子:confemailspayproviderswarehouse。包名應儘量簡短並能讓人一目瞭然。

每個包都有自己的 Setup() 函數。該函數只接收能讓該包運行的最基本的參數。例如,如果包對外提供 HTTP 服務,那麼 Setup() 函數則僅需要接受一個類似 mux route 的 HTTP route。當包需要訪問數據庫時,Setup() 函數也是隻接受 sql.DB 參數就可以了。當然,這個包也可能需要依賴另一個包。

包內的組成

知道了模塊的外部依賴,下一步我們就可以專注於如何在模塊內組織代碼(包括相關依賴的處理)。在最開始,這個包包含以下文件: setup.go - 其中包含 Setup() 函數, service.go - 它是邏輯文件, repository.go - 它是在讀取 / 保存數據到數據的的文件。

Setup() 函數負責構建模塊的每個構建塊,即服務、存儲庫、註冊事件處理程序或 HTTP 處理程序等等。這是使用這種方法的實際生產代碼的一個例子。

func Setup(router *mux.Router, httpClient httpGetter, auth jwtmiddleware.Authorization, logger logger) {
 h := httpHandler{
  logger:        logger,
  requestClaims: jwtutil.NewHTTPRequestClaims(client),
  service:       service{client: httpClient},
 }
 auth.CreateRoute("/v1/lastAnswerTime", h.proxyRequest, http.MethodGet)
}

以上代碼中, 它構建了 JWT 中間件,這是一個處理所有業務邏輯 (以及日誌的位置) 並註冊 HTTP 處理程序的服務。正因爲如此,模塊是非常獨立的,並且 (理論上) 可以轉移到單獨的微服務中,而不需要做太多工作。最後,所有的包都在 main 函數中配置。

有時,我們需要一些處理程序或數據庫驅動。例如,一些信息可以被存儲在數據庫中,然後通過事件發送到平臺的不同部分。使用像 saveToDb() 這樣的方法將數據只保存在同一個庫中是很不方便的。所有類似的元素都應該由以下功能分割: repository_order.go 或 service_user.go。如果對象的類型超過 3 種,則將其移動到單獨的子文件夾中。

測試

說到測試,我堅持一些原則。首先,在 Setup() 函數中使用接口。這些接口應該儘可能小。在上面的例子中,有一個 httpGetter 接口。接口中只有 Get() 函數。

type httpGetter interface {
 Get(url string) (resp *http.Response, err error)
}

謝天謝地,我只需要模擬一個方法。接口的定義需要儘可能地接近它的用途。

其次,嘗試編寫更少的測試用例的同時可以覆蓋到更多的代碼。對於每個主函數的決策 / 操作,一個成功的測試用例和一個失敗的測試用例應該足夠覆蓋大約 80% 的代碼。有時,程序中有一些關鍵部分,這部分可以被單獨的測試用例覆蓋。

最後,在以 _test 爲後綴的單獨包中編寫測試,並將其放入模塊中。把所有的東西都放在一個地方是很有用的。

當您想要測試整個應用程序時,請在主函數旁邊的 setup() 函數中準備好每個依賴項。它將爲生產環境和測試環境提供相同的設置,可以爲您避免一些 bug。測試應該重用 setup() 函數,並且只模擬那些不易模擬的依賴項 (比如外部 api)。

總結

所有其他文件(比如 .travis.yaml 等)都保存在項目根目錄中。這讓我對整個項目有了一個清晰的認識。讓我知道在哪裏可以找到主文件,在哪裏可以找到與基礎結構相關的文件,並且沒有混合在一起。否則,項目的主文件夾就會變得一團糟。

正如我在介紹中所說,我知道並非所有項目都能從中受益,但是像 microservices 這樣的小型程序會發現它非常有用。


via: https://developer20.com/how-i-organize-packages-in-go/

作者:Bartłomiej Klimczak[1] 譯者:shadowstorm97[2] 校對:DingdingZhou[3]

本文由 GCTT[4] 原創編譯,Go 中文網 [5] 榮譽推出,發佈在 Go 語言中文網公衆號,轉載請聯繫我們授權。

參考資料

[1]

Bartłomiej Klimczak: https://developer20.com/about-me/index.html

[2]

shadowstorm97: https://github.com/shadowstorm97

[3]

DingdingZhou: https://github.com/DingdingZhou

[4]

GCTT: https://github.com/studygolang/GCTT

[5]

Go 中文網: https://studygolang.com/

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/Y-tQ8AYMGHEgXphKVK1afA