你的項(xiàng)目應(yīng)該如何正確分層?你會(huì)嗎?
談到應(yīng)用程序的分層架構(gòu),很多人首先想到的是一個(gè)標(biāo)準(zhǔn)的模型,包括控制器(Controller)、服務(wù)層(Service)和數(shù)據(jù)訪問(wèn)層(Mapper)三個(gè)主要部分。這聽(tīng)起來(lái)似乎很直觀和簡(jiǎn)單,但實(shí)際上,很多開(kāi)發(fā)者在實(shí)施時(shí)并沒(méi)有明確區(qū)分這些層次的具體職責(zé)。例如,一些項(xiàng)目中,控制器層的代碼量反而超過(guò)了服務(wù)層,而服務(wù)層僅僅作為一個(gè)傳輸介質(zhì),這反映了開(kāi)發(fā)過(guò)程中容易被忽視的問(wèn)題。這種模糊的層級(jí)職責(zé)劃分,最終可能導(dǎo)致架構(gòu)的混亂,使得代碼難以復(fù)用和維護(hù)
如何進(jìn)行分層
一個(gè)好的應(yīng)用分層需要具備以下幾點(diǎn):
方便后續(xù)代碼進(jìn)行維護(hù)擴(kuò)展;
分層的效果需要讓整個(gè)團(tuán)隊(duì)都接受;
各層的職責(zé)邊界清晰。
圖片
阿里巴巴的編碼規(guī)范細(xì)化了應(yīng)用程序架構(gòu)的多個(gè)層次,旨在更清楚地界定各層的職責(zé)和作用。這些層次包括:
開(kāi)放接口層:這一層負(fù)責(zé)將服務(wù)層的功能通過(guò)RPC接口或HTTP接口向外暴露,同時(shí)負(fù)責(zé)網(wǎng)關(guān)的安全和流量控制。
終端顯示層:負(fù)責(zé)在各種客戶端上渲染和顯示信息,使用不同的技術(shù)如Velocity、JavaScript、JSP進(jìn)行頁(yè)面渲染和移動(dòng)端展示。
Web層:處理訪問(wèn)控制和轉(zhuǎn)發(fā),進(jìn)行基本的參數(shù)校驗(yàn)和一些不需要復(fù)用的簡(jiǎn)單業(yè)務(wù)處理。
服務(wù)層(Service層):執(zhí)行更具體的業(yè)務(wù)邏輯處理。
管理層(Manager層):作為一個(gè)通用業(yè)務(wù)處理層,具備三個(gè)主要功能:封裝對(duì)第三方平臺(tái)的調(diào)用、下沉Service層的通用能力(如緩存和中間件處理)、以及與數(shù)據(jù)訪問(wèn)層(DAO層)的交互,實(shí)現(xiàn)對(duì)數(shù)據(jù)訪問(wèn)對(duì)象的復(fù)合使用。
數(shù)據(jù)訪問(wèn)層(DAO層):直接與數(shù)據(jù)庫(kù)(如MySQL、Oracle、Hbase)進(jìn)行交互的層級(jí)。
優(yōu)化分層
首先需要說(shuō)明的是,如果 RPC 框架選用 Thrift,可能會(huì)比其他的 RPC 框架多出一層,作用和 Controller 層類似:
圖片
阿里巴巴的架構(gòu)分層規(guī)范中,最頂層由 Controller 和 TService 構(gòu)成,主要職責(zé)是處理輕量級(jí)的業(yè)務(wù)邏輯、進(jìn)行參數(shù)驗(yàn)證和異常管理。
這一層設(shè)計(jì)的目的是保持足夠的靈活性,以便在需要時(shí)可以方便地更改或替換接口類型,因此這里的業(yè)務(wù)邏輯應(yīng)盡可能簡(jiǎn)化,有時(shí)甚至可以避免實(shí)現(xiàn)具體邏輯。
緊接著的是 Service 層,承擔(dān)著具體的業(yè)務(wù)邏輯處理。在這個(gè)層級(jí),建議采取一種方法:讓每一個(gè) Controller 操作都對(duì)應(yīng)一個(gè) Service 方法。
這樣做的好處是避免將業(yè)務(wù)邏輯的編排混入 Controller 層,從而在將來(lái)需要接入其他接口類型,如 Thrift 時(shí),可以避免重復(fù)編排業(yè)務(wù)邏輯,減少代碼的重復(fù)和維護(hù)成本。這種方法強(qiáng)調(diào)了在不同層之間保持清晰的職責(zé)分離,以提高代碼的可維護(hù)性和可擴(kuò)展性。
圖片
這樣大量的重復(fù)工作必定會(huì)導(dǎo)致開(kāi)發(fā)效率下降,所以你要把業(yè)務(wù)編排邏輯都放進(jìn) Service 層中。
圖片
接下來(lái)是 Manager 層,這一層充當(dāng)了可復(fù)用邏輯的核心角色。在這個(gè)層面上,Manager 可以是負(fù)責(zé)單一功能的服務(wù),如緩存(Cache)、消息隊(duì)列(MQ)等;同時(shí),它也能夠處理更復(fù)雜的任務(wù),比如當(dāng)需要同時(shí)調(diào)用多個(gè) Manager 服務(wù)時(shí),可以將它們組合成一個(gè)綜合性的 Manager,以處理更為復(fù)雜的業(yè)務(wù)邏輯,如在邏輯上進(jìn)行類似于數(shù)據(jù)庫(kù)連表查詢的操作。
再來(lái)看 DAO 層,這是數(shù)據(jù)庫(kù)訪問(wèn)層。主要負(fù)責(zé)“操作數(shù)據(jù)庫(kù)的某張表,映射到某個(gè) Java 對(duì)象”,DAO 應(yīng)該只允許自己的 Service 訪問(wèn)。
在阿里巴巴的編碼規(guī)范中,分層領(lǐng)域模型的轉(zhuǎn)換是一個(gè)關(guān)鍵的設(shè)計(jì)考慮,以確保數(shù)據(jù)在不同層之間傳遞時(shí)的清晰性和準(zhǔn)確性。以下是一些核心領(lǐng)域模型及其用途的概述:
DO(Data Object):數(shù)據(jù)對(duì)象,直接與數(shù)據(jù)庫(kù)表結(jié)構(gòu)對(duì)應(yīng),通過(guò) DAO(數(shù)據(jù)訪問(wèn)對(duì)象)層傳輸數(shù)據(jù)源對(duì)象。這確保了數(shù)據(jù)層與數(shù)據(jù)庫(kù)的直接映射,便于操作數(shù)據(jù)庫(kù)。
DTO(Data Transfer Object):數(shù)據(jù)傳輸對(duì)象,用于服務(wù)層或管理層向外部傳輸?shù)膶?duì)象。DTO 主要用于跨層通訊,封裝了需要傳輸?shù)臄?shù)據(jù),有助于減少一個(gè)方法調(diào)用所需要傳遞的參數(shù)數(shù)量,簡(jiǎn)化遠(yuǎn)程接口調(diào)用。
BO(Business Object):業(yè)務(wù)對(duì)象,由服務(wù)層輸出,封裝了業(yè)務(wù)邏輯的對(duì)象。BO 體現(xiàn)了業(yè)務(wù)模型的概念,通常用于封裝具體的業(yè)務(wù)邏輯和業(yè)務(wù)狀態(tài),反映了業(yè)務(wù)操作的結(jié)果。
AO(Application Object):應(yīng)用對(duì)象,位于 Web 層與服務(wù)層之間,是一個(gè)抽象的復(fù)用對(duì)象模型,非常貼近于展示層但復(fù)用度不高。AO 主要用于處理特定于應(yīng)用的邏輯和狀態(tài),作為不同服務(wù)層之間數(shù)據(jù)傳輸?shù)闹虚g層。
VO(View Object):視圖對(duì)象,通常由 Web 層傳輸至模板渲染引擎層的對(duì)象。VO 主要用于展示層數(shù)據(jù)的封裝,專門(mén)為用戶界面定制,包含了用戶界面展示所需的數(shù)據(jù)。
Query:數(shù)據(jù)查詢對(duì)象,用于在各層之間傳遞查詢請(qǐng)求。它允許將查詢條件封裝為一個(gè)對(duì)象,使得方法調(diào)用更加清晰,同時(shí)避免了使用諸如 Map 這類無(wú)結(jié)構(gòu)的數(shù)據(jù)類型來(lái)傳遞多個(gè)查詢條件,提高了代碼的可讀性和可維護(hù)性。
圖片
每一個(gè)層基本都有自己對(duì)應(yīng)的領(lǐng)域模型,而有些人過(guò)于追求每一層都用自己的領(lǐng)域模型,這就導(dǎo)致在一次請(qǐng)求中,出現(xiàn)多次對(duì)象轉(zhuǎn)換。
一個(gè)折中的方案是:
允許 Service/Manager 可以操作數(shù)據(jù)領(lǐng)域模型。
Controller/TService 層的領(lǐng)域模型不允許傳入 DAO 層,這樣就不符合職責(zé)劃分了。
同理,不允許 DAO 層的數(shù)據(jù)傳入到 Controller/TService。