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