Go 項目怎么做好分層架構和目錄規劃
開發項目的時候我們都愛說XX模塊,模塊一般是跟著項目所服務的業務走的。而項目的分層則沒有那么依賴具體的業務類型,靠一些軟件設計的方法論和經驗在項目搭建初期就能大體確定其結構。
我給大家介紹一下Go項目的分層架構設計,把整個項目的結構按職能進行劃分,規劃出整個項目的目錄結構。
圖片
分層架構
談到給項目的代碼分層,必然少不了對分層架構的回顧。分層架構如下圖所示
圖片
分層架構的一個重要原則是:每層只能與位于其下方的層發生耦合。我們大多數時候使用的是松散型分層架構,允許上層與任意下層發生耦合。
這里說的耦合可以先理解成包和包之間的引用關系,這樣更好理解一些。所以在我們設計項目的結構時,要注意下層的package 一定不能引用上層的package。使用松散型分層架構的目的是讓我們的設計能更靈活,必要時出現跨層直接訪問的情況也是被允許的。注意哦,不是推薦我們有事兒沒事都直接在用戶接口層訪問DAO查數據哦。
舉個例子假如有個舊項目把很多東西都寫在了controller里,又假如你是那個接過來要負責它的苦命人,你本來下定決心以后的新代碼都好好寫不能再這么潦草下去啦,比如說你把把一些新的邏輯放到service里。
但是業務系統一般都是在老需求基礎上迭代,新老代碼會有調用關系,這時候你卻發現原來的邏輯都在controller里,那這時你要不把用到的老邏輯往service放一份,要不你也徹底放棄往controller直接寫完事兒啦,你咋選?
項目排期那么緊,我估計換誰都是徹底放棄,就往controller里寫吧。所以在項目搭建的開始階段就確定后分層結構還是很有必要的,后期做需求開發時就可以相對無腦一些按照層次結構往里面套,不同的邏輯寫到不同的層里。
上面這個例子是不是很好的體現了大家平時在公司接管項目初期的心理呀,我相信多少人都遇到過這種情況。
好了,回到主題,下面簡單說一下分層架構中各個層的職責。
用戶接口層:
用戶接口層只用于處理用戶界面顯示和用戶的請求響應,針對后端API服務,基本上該層就是負責接受用戶請求、驗證請求、調用下層拿到結果返回響應,在這里不應該包含核心業務邏輯。
應用層
應用層里面是應用服務,主要負責用例流的任務協調,每個用例流對應一個服務方法(可以理解為API接口),應用服務是領域服務的直接調用者,它主要協調對領域服務的操作,同時像發送基于某個事件的消息通知、發郵件、短信給用戶等操作都會寫在應用層,這樣能讓領域服務能專注于核心的業務邏輯。
應用服務還有一個作用是,當一個API的邏輯需要多個領域服務一起協作來完成時,一個清晰的解決方案是通過應用服務來對多個領域服務來進行協調調用。
圖片
領域層
領域層是真正寫業務邏輯的地方,這個業務邏輯可以理解成本領域的核心業務邏輯,比如怎么通過CRUD完成某件事寫在這里,而成功或者失敗后向什么地方推送消息通知、調用其他領域服務、請求其他API 這些核心之外的業務邏輯則寫在應用層的應用服務里,領域層只關注本領域里的業務邏輯,應用層負責協調調度它們。
基礎層
基礎層放置我們為項目提供的一些公共、通用的能力:數據的訪問和持久化、對接第三方平臺能力而封裝的庫、為項目開發的基礎組件等都放在這一層。
注意這里說的層都是概念性的,不是指具體項目中的某個目錄或者package。
分層后的目錄結構
我們的Go項目,按照分層架構進行規劃后,可以用下面這張圖表示。
圖片
圖中的邏輯層我是用虛線框住的,代表所有與邏輯相關的應該放在應用和領域層中,它們邏輯側重點有些不同,上面我們已經說過應用和領域層的區別了,我們在專欄教程里還有更多的實際需求的例子來體現它們之間的區別。
整個項目按分層架構以及各種實際功能的需要,目錄結構的規劃如下
.
|---api
| |---controller # 控制器
| |---reply # 響應對象
| |---request # 請求對象
| |---router # 路由
|---common
| |---app # 分頁和接口響應處理
| |---enum # 枚舉
| |---errcode # 項目錯誤管理
| |---logger # 項目的日志門面
| |---middleware # 中間件
| |---util # 輔助函數
|---config # 配置
|---dal # 數據訪問層
| |---cache # 緩存
| |---dao # 數據訪問對象
| |---model # 數據模型對象
|---event
|---library
|---logic # 邏輯層
| |---appservice # 應用服務
| |---domainservice # 領域服務
| |---do # 領域對象
|---resources # 資源目錄
|---test # 測試腳本
怎么防止分層"塌陷”
代碼有了分層后,如果使用不當一定會導致分層塌陷,最后還是把代碼寫成一坨,那怎么能盡量減少這在情況出現呢?除了"各個層職責單一"的片湯話外其實是有明確的辦法的,老外把這個東西叫做防腐層。
防腐層有很多種,簡單和最常用的就是各種數據對象, 他們之間的轉化讓各個層都能獨立的發展,能最大限度避免代碼層的塌陷。
項目中設計了四種數據對象:請求對象,數據實體Model對象,領域對象和響應對象
下面這張圖展示了一個完整的API請求中客戶端與服務的完整交互過程中每種數據對象產生的時段和位置。根據API請求、邏輯的復雜程度我們可以有選擇的選擇其中幾個對象完成接口的請求和響應數據的返回。
圖片
通過上面四種數據對象,程序的每個分層都可以專注自己的事兒,DAO層、Service層不必考慮接口要返回什么格式,用戶接口層也不用怕把一些不該暴露的字段數據給暴露了出去。