代碼分層的設計

分層思想,是應用系統最常見的一種架構模式,我們會將系統橫向切割,根據業務職責劃分。MVC 三層架構就是非常典型架構模式,劃分的目的是規劃軟件系統的邏輯結構便于開發維護。MVC:英文即 Model-View-Controller,分成模型層、視圖層、控制層。將頁面和業務邏輯分離,提高應用的可擴展性及可維護性。如圖所示。
事實上,MVC 三層架構只是概念層面的指導思想,我們會將層次結構劃分的更加細致。例如,傳統后端的 MVC 模式對于前后端的劃分界限比較模糊。一般情況下,前端開發人員負責編寫項目的靜態頁面,包括 HTML 頁面、CSS 樣式與 JavaScript 交互部分,并提供給服務端開發人員編寫視圖層業務,甚至有的項目直接讓前端開發人員完成視圖層的業務開發任務。這樣的開發模式造成的問題在于,前后端在開發過程中分工不明確,并且存在相互強依賴,前端開發人員需要關心服務端的業務,服務端開發人員也需要依賴前端的進度。并且隨著 Android、 IOS、 PC 以及 U3D 等多個客戶端加入,程序的開發成本與維護成本會指數級上升。為了提高開發效率,細化職責,前后端分離的需求越來越被重視。前后端分離在于服務端提供 API 接口,前端調用 AJAX 實現數據交互。如圖所示。

此外,隨著數據存儲能力的不斷擴展(MySQL、Oracle、Redis、MongoDB、ElasticSearch、PostgreSQL、HBase 等),以及隨著微服務的流行與普及,我們經常通過 RPC(Dubbo、HSF、Thrift 等)依賴很多外部接口或 HTTP 調用第三方平臺。因此,我們需要一套細致劃分的代碼結構。此外,很多時候,我們在開發過程中,也并沒有把它們職責劃分開。例如,在代碼結構中,我們將非常多的邏輯業務放在了 Controller 層,而只把 Service 作為數據透傳的途徑了。事實上,這個是不對的。無獨有偶,我們還會發現有的項目中在 Dao 層調用遠程服務,也有的會在 Service 層或者 Controller 層進行這樣的操作,由于不同研發同學的習慣不同,或者偷工取巧導致開發代碼風格完全不同,代碼層次結構混亂。

總結一下,MVC 三層架構只是概念層面的指導思想,我們會將層次結構劃分的更加細致。現在,我們來深入探討“如何合理的設計代碼分層,論代碼分層的設計之道”。在筆者看來,合理的代碼分層應該是這樣的。如圖所示。

其中,數據持久層 承載了數據存儲和訪問的能力,它既與底層數據進行交換,包括 MySQL、Oracle、Redis、MongoDB、ElasticSearch、PostgreSQL、HBase 等,又通過 Pxoxy 的代理和包裝與遠程服務數據進行聯動。因此,在業務邏輯層調用時,它對底層的數據實現方式是無感知的,無論是哪種數據存儲方式,以及它是遠程數據,還是本地數據,都可以非常容易的調用。換句話說,我們需要將數據的查詢和更改操作限制在數據持久層,并只能被業務邏輯層訪問。

那么,業務邏輯層 的職責是與數據持久層交互,對多個數據源的操作進行聚合,并且提供組合復用的能力。此外,它也是業務通用能力的處理層,其中還包括緩存方案、消息監聽(MQ)、定時任務等等。此外,我們要將盡可能多的業務處理放在業務邏輯層,包括了參數校驗、數據轉換、異常處理等,而不是在 Controller 再去處理。

筆者認為:請求處理層具有三塊能力,一個是通過模板引擎渲染,例如 FreeMarket、Velocity 的頁面渲染,以及通過 Controller 層封裝的 RESTful API 的 HTTP 接口。如果項目中用到了 Dubbo、HSF、Thrift 等 RPC 服務,我們還需要提供對于的服務給上游的業務方使用,它通過 Service 來實現并暴露成 RPC 接口。這里,Service 的命名是相對的,一般通過 Client 提供接口,通過 Service 實現具體的業務邏輯。

我們了解了邏輯結構,那么,筆者認為比較清晰的物理代碼結構應該是這樣的。

那么,我們可以跨層級調用嗎?筆者認為:我們需要禁止跨層級調用,因為每個層級都自己的職責,并且對上層而言是透明的,就像 OSI 七層協議模型和 TCP/IP 四層協議模型一樣,只有將職責限制在自己的邊界內,整體層次結構才清晰明了。那么對于同級調用,筆者認為在業務邏輯層是允許的,但是,要特別注意循環調用的產生。

現在,我們再橫向理解幾個領域模型:VO、BO、DO、DTO。這個概念,是由阿里編碼規約提到的,由于其業務非常復雜,因此為了更好地進行領域建模和模型隔離,提出了這幾個概念。其中,DO(Data Object)與數據庫表結構一一對應,通過 DAO 層向上傳輸數據源對象。 而 DTO(Data Transfer Object)是遠程調用對象,它是 RPC 服務提供的領域模型。注意的是,對于 DTO 一定要保證其序列化,實現 Serializable 接口,并顯示提供 serialVersionUID,否則在反序列化時,如果 serialVersionUID 被修改,那么反序列化會失敗。事實上,DO 和 DTO 唯一的區別在于,一個是本地數據源的領域模型,一個是遠程服務的序列化領域模型。對于 BO(Business Object),它是業務邏輯層封裝業務邏輯的對象,一般情況下,它是聚合了多個數據源的復合對象。那么,VO(View Object) 通常是請求處理層傳輸的對象,它通過 Spring 框架的轉換后,往往是一個 JSON 對象。例如,你需要解決 Long 類型的數據精度丟失的問題(如果直接傳給 Web 端的話,在 Long 長度大于 17 位時會出現精度丟失),你就可以在 Controller 層通過 @ResponseBody 將返回數據自動轉換成 JSON 時,統一封裝成字符串。

總結一下,分層思想,將系統橫向切割,根據業務職責劃分。劃分的目的是規劃軟件系統的邏輯結構便于開發維護。但是,隨著微服務的演變和不同研發的編碼習慣,往往導致了代碼分層不徹底導致引入了“壞味道”。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/249525.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/249525.shtml
英文地址,請注明出處:http://en.pswp.cn/news/249525.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【24小時內第四更】為什么我們要堅持寫博客?

前言 從2018年7月份,我開始了寫作博客之路。開始之前,我打算分享下之前的經歷。去年初公司來了個架構師,內部分享過docker原理,TDD單元測試驅動,并發并行異步編程等內容,讓我著實驚呆了,因為確實…

sqoop快速入門

轉自http://www.aboutyun.com/thread-22549-1-1.html 轉載于:https://www.cnblogs.com/drjava/p/10473297.html

ListableBeanFactory接口

ListableBeanFactory獲取bean時,Spring 鼓勵使用這個接口定義的api. 還有個Beanfactory方便使用.其他的4個接口都是不鼓勵使用的. 提供容器中bean迭代的功能,不再需要一個個bean地查找.比如可以一次獲取全部的bean(太暴力了),根據類型獲取bean.在看SpringMVC時,掃描包路徑下的…

HDU 4035 Maze

Maze http://acm.hdu.edu.cn/showproblem.php?pid4035 分析: 在樹上走來走去,然后在一個點可以k的概率回到1,可以e的概率走出去,可以1-k-e的概率走到其他的位置(分為父節點和子節點討論)。 轉移方程就是&a…

面向對象之三大特性:繼承,封裝,多態

python面向對象的三大特性:繼承,封裝,多態。 1. 封裝: 把很多數據封裝到?個對象中. 把固定功能的代碼封裝到?個代碼塊, 函數, 對象, 打包成模塊. 這都屬于封裝的思想. 具體的情況具體分析. 比如. 你寫了?個很?B的函數. 那這個也可以被稱為…

configurablebeanfactory

ConfigurableBeanFactory定義BeanFactory的配置.ConfigurableBeanFactory中定義了太多太多的api,比如類加載器,類型轉化,屬性編輯器,BeanPostProcessor,作用域,bean定義,處理bean依賴關系,合并其他ConfigurableBeanFactory,bean如何銷毀. ConfigurableBeanFactory同時繼承了Hi…

Xlua文件在熱更新中調用方法

Xlua文件在熱更新中調用方法 public class news : MonoBehaviour { LuaEnv luaEnv;//定義Lua初始變量 void Awake() { luaEnv new LuaEnv();//new開辟空間 luaEnv.AddLoader(myload);//調用方法地址、返回字節 luaEnv.DoString("requirefish");//更新文件 } void O…

springboot 使用的配置

1,控制臺打印sql logging:level:com.sdyy.test.mapper: debug 2,開啟駝峰命名 mybatis.configuration.map-underscore-to-camel-casetrue 轉載于:https://www.cnblogs.com/xiaohu1218/p/10477318.html

AutowireCapableBeanFactory接口

AutowireCapableBeanFactory在BeanFactory基礎上實現了對存在實例的管理.可以使用這個接口集成其它框架,捆綁并填充并不由Spring管理生命周期并已存在的實例.像集成WebWork的Actions 和Tapestry Page就很實用. 一般應用開發者不會使用這個接口,所以像ApplicationContext這樣的…

外觀模式

一、什么是外觀模式   有些人可能炒過股票,但其實大部分人都不太懂,這種沒有足夠了解證券知識的情況下做股票是很容易虧錢的,剛開始炒股肯定都會想,如果有個懂行的幫幫手就好,其實基金就是個好幫手,支付寶…

OC內存管理

OC內存管理 一、基本原理 (一)為什么要進行內存管理。 由于移動設備的內存極其有限,所以每個APP所占的內存也是有限制的,當app所占用的內存較多時,系統就會發出內存警告,這時需要回收一些不需要再繼續使用的…

cf1132E. Knapsack(搜索)

題意 題目鏈接 Sol 看了status里面最短的代碼。。感覺自己真是菜的一批。。直接爆搜居然可以過&#xff1f;。。但是現在還沒終測所以可能會fst。。 #include<bits/stdc.h> #define Pair pair<int, int> #define MP(x, y) make_pair(x, y) #define fi first #defi…

ConfigurableListableBeanFactory

ConfigurableListableBeanFactory 提供bean definition的解析,注冊功能,再對單例來個預加載(解決循環依賴問題). 貌似我們一般開發就會直接定義這么個接口了事.而不是像Spring這樣先根據使用情況細分那么多,到這邊再合并 ConfigurableListableBeanFactory具體&#xff1a; 1、…

焦旭超 201771010109《面向對象程序設計課程學習進度條》

《2018面向對象程序設計&#xff08;java&#xff09;課程學習進度條》 周次 &#xff08;閱讀/編寫&#xff09;代碼行數 發布博客量/博客評論量 課堂/課余學習時間&#xff08;小時&#xff09; 最滿意的編程任務 第一周 50/20 1/0 6/4 九九乘法表 第二周 90/5…

面試題集錦

1. L1范式和L2范式的區別 (1) L1范式是對應參數向量絕對值之和 (2) L1范式具有稀疏性 (3) L1范式可以用來作為特征選擇&#xff0c;并且可解釋性較強&#xff08;這里的原理是在實際Loss function 中都需要求最小值&#xff0c;根據L1的定義可知L1最小值只有0&#xff0c;故可以…

Spring注解配置工作原理源碼解析

一、背景知識 在【Spring實戰】Spring容器初始化完成后執行初始化數據方法一文中說要分析其實現原理&#xff0c;于是就從源碼中尋找答案&#xff0c;看源碼容易跑偏&#xff0c;因此應當有個主線&#xff0c;或者帶著問題、目標去看&#xff0c;這樣才能最大限度的提升自身代…

halt

關機 init 0 reboot init6 shutdown -r now 重啟 -h now 關機 轉載于:https://www.cnblogs.com/todayORtomorrow/p/10486123.html

Spring--Context

應用上下文 Spring通過應用上下文&#xff08;Application Context&#xff09;裝載bean的定義并把它們組裝起來。Spring應用上下文全權負責對象的創建和組裝。Spring自帶了多種應用上下文的實現&#xff0c;它們之間主要的區別僅僅在于如何加載配置。 1.AnnotationConfigApp…

了解PID控制

2019-03-07 【小記】 了解PID控制 比例 - 積分 - 微分 積分 --- 記憶過去 比例 --- 了解現在 微分 --- 預測未來 轉載于:https://www.cnblogs.com/skullboyer/p/10487884.html

program collections

Java byte & 0xff byte[] b new byte[1];b[0] -127;System.out.println("b[0]:"b[0]"; b[0]&0xff:"(b[0] & 0xff));//output:b[0]:-127; b[0]&0xff:129計算機內二進制都是補碼形式存儲&#xff1a; b[0]: 補碼&#xff0c;10000001&…