新聞中心
框架的工程設(shè)計(jì)沒有采用復(fù)雜的設(shè)計(jì)思路,而是采用了務(wù)實(shí)、穩(wěn)健和成熟的工程設(shè)計(jì),以快速解決業(yè)務(wù)痛點(diǎn)、降低開發(fā)維護(hù)成本為第一考量。

一、工程目錄結(jié)構(gòu)
?GOFrame?業(yè)務(wù)項(xiàng)目基本目錄結(jié)構(gòu)如下(以?Single Repo?為例):
/
├── api
├── internal
│ ├── cmd
│ ├── consts
│ ├── controller
│ ├── model
│ │ └── entity
│ └── service
│ └── internal
│ ├── dao
│ └── do
├── manifest
├── resource
├── utility
├── go.mod
└── main.go
工程目錄采用了通用化的設(shè)計(jì),實(shí)際項(xiàng)目中可以根據(jù)項(xiàng)目需要適當(dāng)增減模板給定的目錄。例如,沒有i18n及template需求的場景,直接刪除對應(yīng)目錄即可。
| 目錄/文件名稱 | 說明 | 描述 |
| api | 接口定義 | 對外提供服務(wù)的輸入/輸出數(shù)據(jù)結(jié)構(gòu)定義??紤]到版本管理需要,往往以api/v1...存在 |
| internal | 內(nèi)部邏輯 | 業(yè)務(wù)邏輯存放目錄。通過Golang internal特性對外部隱藏可見性 |
| -cmd | 入口指令 | 命令行管理目錄。可以管理維護(hù)多個命令行 |
| -consts | 常量定義 | 項(xiàng)目所有常量定義 |
| -controller | 接口處理 | 接收/解析用戶輸入?yún)?shù)的入口/接口層 |
| -model | 結(jié)構(gòu)模型 | 數(shù)據(jù)結(jié)構(gòu)管理模塊,管理數(shù)據(jù)實(shí)體對象,以及輸入與輸出數(shù)據(jù)結(jié)構(gòu)定義 |
| -entity | 數(shù)據(jù)模型 | 數(shù)據(jù)模型是模型與數(shù)據(jù)集合的一對一關(guān)系,由工具維護(hù),用戶不能修改 |
| -service | 邏輯封裝 | 業(yè)務(wù)邏輯封裝管理,特定的業(yè)務(wù)邏輯實(shí)現(xiàn)和封裝 |
| -dao | 數(shù)據(jù)訪問 | 數(shù)據(jù)訪問對象,這是一層抽象對象,用于和底層數(shù)據(jù)庫交互,僅包含最基礎(chǔ)的CURD方法 |
| -do | 領(lǐng)域?qū)ο?/td> | 用于dao數(shù)據(jù)操作中業(yè)務(wù)模型與實(shí)例模型轉(zhuǎn)換,由工具維護(hù),用戶不能修改 |
| mainfest | 交付清單 | 包含程序編譯、部署、運(yùn)行、配置的文件 |
| -config | 配置管理 | 配置文件存放目錄 |
| -docker | 鏡像文件 | Docker鏡像相關(guān)依賴文件,腳本文件等等 |
| -deploy | 部署文件 | 部署相關(guān)的文件。默認(rèn)提供了Kubernetes集群化部署的Yaml模板,通過kustomize管理 |
| resource | 靜態(tài)資源 | 靜態(tài)資源文件。這些文件往往可以通過資源打包/鏡像編譯的形式注入到發(fā)布文件中 |
| go.mod | 依賴管理 | 使用Go Module包管理的依賴描述文件 |
| main.go | 入口文件 | 程序入口文件 |
業(yè)務(wù)接口 - api
業(yè)務(wù)接口包含兩部分:接口定義(?api?)+接口實(shí)現(xiàn)(?controller?)。
?api?包的職責(zé)類似于三層架構(gòu)設(shè)計(jì)中的UI表示層,負(fù)責(zé)接收并響應(yīng)客戶端的輸入與輸出,包括對輸入?yún)?shù)的過濾、轉(zhuǎn)換、校驗(yàn),對輸出數(shù)據(jù)結(jié)構(gòu)的維護(hù),并調(diào)用 ?service實(shí)現(xiàn)業(yè)務(wù)邏輯處理。
邏輯封裝 - service
?service?包的職責(zé)類似于三層架構(gòu)設(shè)計(jì)中的?BLL?業(yè)務(wù)邏輯層,負(fù)責(zé)具體業(yè)務(wù)邏輯的實(shí)現(xiàn)以及封裝。
數(shù)據(jù)訪問 - dao
?dao?包的職責(zé)類似于三層架構(gòu)中的?DAL?數(shù)據(jù)訪問層,數(shù)據(jù)訪問層負(fù)責(zé)所有的數(shù)據(jù)訪問收口。
結(jié)構(gòu)模型 - model
?model?包的職責(zé)類似于三層架構(gòu)中的?Model?模型定義層。模型定義代碼層中僅包含全局公開的數(shù)據(jù)結(jié)構(gòu)定義,往往不包含方法定義。
這里需要注意的是,這里的?model?不僅負(fù)責(zé)維護(hù)數(shù)據(jù)實(shí)體對象(?entity?)結(jié)構(gòu)定義,也包括所有的輸入/輸出數(shù)據(jù)結(jié)構(gòu)定義,被?api/dao/service?共同引用。這樣做的好處除了可以統(tǒng)一管理公開的數(shù)據(jù)結(jié)構(gòu)定義,也可以充分對同一業(yè)務(wù)領(lǐng)域的數(shù)據(jù)結(jié)構(gòu)進(jìn)行復(fù)用,減少代碼冗余。
三層架構(gòu)設(shè)計(jì)與框架代碼分層映射關(guān)系
二、請求分層流轉(zhuǎn)
cmd
?cmd?層負(fù)責(zé)引導(dǎo)程序啟動,顯著的工作是初始化邏輯、注冊路由對象、啟動?server?監(jiān)聽、阻塞運(yùn)行程序直至?server?退出。
api
上層?server?服務(wù)接收客戶端請求,轉(zhuǎn)換為?api?中定義的?Req?接收對象、執(zhí)行請求參數(shù)到?Req?對象屬性的類型轉(zhuǎn)換、執(zhí)行?Req?對象中綁定的基礎(chǔ)校驗(yàn)并轉(zhuǎn)交?Req?請求對象給?controller?層。
controller
?controller?層負(fù)責(zé)接收?Req?請求對象后做一些業(yè)務(wù)邏輯校驗(yàn),隨后調(diào)用一個或多個?service?實(shí)現(xiàn)業(yè)務(wù)邏輯,將執(zhí)行結(jié)構(gòu)封裝為約定的?Res?數(shù)據(jù)結(jié)構(gòu)對象返回。
model
?model?層中管理了所有的業(yè)務(wù)模型,?service?資源的?Input/Output?輸入輸出數(shù)據(jù)結(jié)構(gòu)都由?model?層來維護(hù)。
service
?service?層的業(yè)務(wù)邏輯需要通過調(diào)用?dao?來實(shí)現(xiàn)數(shù)據(jù)的操作,調(diào)用?dao?時需要傳遞?do?數(shù)據(jù)結(jié)構(gòu)對象,用于傳遞查詢條件、輸入數(shù)據(jù)。?dao?執(zhí)行完畢后通過?Entity?數(shù)據(jù)模型將數(shù)據(jù)結(jié)果返回給?service?層。
dao
?dao?層通過框架的?ORM?抽象層組件與底層真實(shí)的數(shù)據(jù)庫交互。
三、常見問題解答
框架是否支持常見的MVC開發(fā)模式
當(dāng)然!
作為一款模塊化設(shè)計(jì)的基礎(chǔ)開發(fā)框架,?GoFrame?不會局限代碼設(shè)計(jì)模式,并且框架提供了非常強(qiáng)大的模板引擎核心組件,可快速用于?MVC?模式中常見的模板渲染開發(fā)。相比較?MVC?開發(fā)模式,在復(fù)雜業(yè)務(wù)場景中,我們更推薦使大家用三層架構(gòu)設(shè)計(jì)模式。
如何清晰界定和管理service和controller的分層職責(zé)
?controller?層處理?Req/Res?外部接口請求。負(fù)責(zé)接收、校驗(yàn)請求參數(shù),并調(diào)用一個或多個 ?service?來實(shí)現(xiàn)業(yè)務(wù)邏輯處理,根據(jù)返回?cái)?shù)據(jù)結(jié)構(gòu)組裝數(shù)據(jù)再返回。
?service?層處理?Input/Output?內(nèi)部方法調(diào)用。負(fù)責(zé)內(nèi)部可復(fù)用的業(yè)務(wù)邏輯封裝,封裝的方法粒度往往比較細(xì)。
因此, 禁止從?controller?層直接透傳?Req?對象給?service?,也禁止?service?直接返回?Res?數(shù)據(jù)結(jié)構(gòu)對象,因?yàn)?service?服務(wù)的主體與?controller?完全不同。當(dāng)您錯誤地使用?service?方法處理特定的?Req?對象的時候,該方法也就與對于的外部接口耦合,僅為外部接口服務(wù),難以復(fù)用。這種場景下?service?替代了?controller?的作用,造成了本末倒置。
如何清晰界定和管理service和dao的分層職責(zé)
這是一個很經(jīng)典的問題。
痛點(diǎn):
- 常見的,開發(fā)者把數(shù)據(jù)相關(guān)的業(yè)務(wù)邏輯實(shí)現(xiàn)封裝到了?
dao?代碼層中,而?service?代碼層只是簡單的?dao?調(diào)用,這么做的話會使得原本負(fù)責(zé)維護(hù)數(shù)據(jù)的?dao?層代碼越來越繁重,反而業(yè)務(wù)邏輯?service?層代碼顯得比較輕。開發(fā)者存在困惑,我寫的業(yè)務(wù)邏輯代碼到底應(yīng)該放到?dao?還是?service?中? - 業(yè)務(wù)邏輯其實(shí)絕大部分時候都是對數(shù)據(jù)的CURD處理,這樣做會使得幾乎所有的業(yè)務(wù)邏輯會逐步沉淀在?
dao?層中,業(yè)務(wù)邏輯的改變其實(shí)會頻繁對?dao?層的代碼產(chǎn)生修改。例如:數(shù)據(jù)查詢在初期的時候可能只是簡單的邏輯,目前代碼放到?dao?好像也沒問題,但是查詢需求增加或變化變得復(fù)雜之后,那么必定會繼續(xù)維護(hù)修改原有的?dao?代碼,同時?service?代碼也可能同時做更新。原本僅限于?service?層的業(yè)務(wù)邏輯代碼職責(zé)與?dao?層代碼職責(zé)模糊不清、耦合較重,原本只需要修改?service?代碼的需求變成了同時修改?service+dao?,使得項(xiàng)目中后期的開發(fā)維護(hù)成本大大增加。
建議:
- 我們的建議。?
dao?層的代碼應(yīng)該盡量保證通用性,并且大部分場景下不需要增加額外方法,只需要使用一些通用的鏈?zhǔn)讲僮鞣椒ㄆ礈惣纯蓾M足。業(yè)務(wù)邏輯、包括看似只是簡單的數(shù)據(jù)操作的邏輯都應(yīng)當(dāng)封裝到?service?中,?service?中包含多個業(yè)務(wù)模塊,每個模塊獨(dú)自管理自己的?dao?對象,?service?與?service?之間通過相互調(diào)用方法來實(shí)現(xiàn)數(shù)據(jù)通信而不是隨意去調(diào)用其他?service?模塊的?dao?對象。
當(dāng)前題目:創(chuàng)新互聯(lián)GoFrame教程:GoFrame工程開發(fā)設(shè)計(jì)-工程目錄設(shè)計(jì)
當(dāng)前URL:http://m.fisionsoft.com.cn/article/cdpppeh.html


咨詢
建站咨詢
