今兒寫這個題目膽子有點大,不過還是得冒險整一篇(我怕您看完了罵我),一是出于經驗分享,另外則是為了后面我們講案例的時候做好鋪墊,好的代碼需要注意的事項其實挺多的,您真讓我一骨腦兒都列出來可能也差點意思,所以遵照我們常態化歪樓的習慣,我是想到哪寫到哪兒,
我沒事兒的時候就喜歡看別人寫的文章,也喜歡看書,識訓還是挺多的,不過歲數大了忘性也大,記不住,可有一個事情我記得倍兒清楚:咱們搞技術的尤其是后端開發都知道一類物件叫視圖模型(View Object簡稱VO),一說VO,大多數文章給的概念就是“后端傳向前端,用于承載需要在頁面上顯示的資訊的物體或物件(反方向是前端傳向后端用于存盤的資訊)”,概念雖然簡單,可是和我的想法就不一樣,誰叫咱這人個色呢,您聽我給您嘮嘮,
我所認為的視圖相對來說要廣義一點:前面您所認為的自然是沒問題;還有一種(咱們以Java為例啊)我來舉例說明:訂單的服務需要呼叫客戶服務來查詢客戶資訊“CustomInfo”,這里面的“CustomInfo”就是視圖模型,您肯定暴起反對:搞笑呢吧?“CustomInfo”是“DTO(資料傳輸物件)”,這種說法當然沒錯了,但DTO這個說法太廣義面且太模糊了,常用的資料物體也算是一種DTO,他承載了需要存盤和從資料庫查詢回傳的資料;前端傳向后端的也是DTO,承載了用戶的輸入,所以您平常老是用DTO這個說法其實真的不夠準確,那為什么說服務間相互傳的資料是VO呢?比如您去相親,對方給您的印象(注意:印象不僅是你主動獲取的,還包括對方傳給你的,就跟兩個函式一樣你呼叫我我呼叫你)比如長相、談吐等這些是對方想讓您知道的(別反對,女人畫起妝來你就不知道她到底長什么樣)關于其自身屬性的部分資訊,當然還有一部分是您從對方身上獲得的資訊,所謂的“印象”是兩種資訊的集成,其實就是資訊視圖,所以您呼叫下游服務時對方回傳給你的就是這個下游服務的視圖也就是下游服務想要展示給你的內容,而且,不僅僅是服務間有視圖,包與包之前也只能通過視圖了解彼此,
上一段我提到的“服務間和包間只能通過視圖了解彼此”,引申的含義是說:服務間和包間只能通過視圖傳遞資訊,這種限制不論是3層還是4層構架都適用,DTO這種稱呼相對模糊,一般我在開發的時候也不會這么叫,實際上,您是喜歡叫VO還是DTO也不耽誤什么事兒,可我們在這里給出了一個重要的約束也就是包或系統間的資料訪問限制,這東西特別容易出亂子,尤其是想把一個服務做二次拆分的時候,如果當前系統無訪問約束那拆的風險就會相當的高,
除了視圖模型外,后端服務開發還會用到另外兩類:資料模型和領域模型,如下圖所示,您別看統共就三種,想用好了沒那么容易,至少在我經歷的不少專案中很多人都是亂用的,要不是沒有訪問限制要不就是模型冗余,

|
重點! 服務間和包間相互呼叫時,傳入和傳出的資訊(簡單欄位除外)只能通過視圖模型進行承載,不得將資料模型和領域模型作為傳輸資訊的載體,包括在訊息中也不可以內嵌領域與資料模型,以避免內部資訊泄露, |
對于模型的濫用,讓我來厚著臉皮做一下糾偏,分別說一個每個物件的作用和主要的使用限制,看完了后您會覺得:“這也太夸張了,寫個代碼有那么麻煩嗎?”,答:有!好東西一般不會是多快好省出來的,
1、資料模型
定義:描述資料庫設計并承載資料在持久層到應用層間之間的傳輸,限制:1)每一張表一個物體;2)級聯查詢的結果一般是多個表的集成,也需要建立對應的物體;3)不可以傳到包、服務之外;4)不要包含任何業務邏輯;5)DAO的輸入輸出只能是簡單欄位或資料模型;6)僅能使用基本型別如Integer、String等,有一個小技巧:如果資料模型和視圖模型的欄位一樣,賦值時可以使用一些工具如“BeanUtils”實作兩個物件間的欄位復制,
2、視圖模型
定義:用于系統間、模塊間、包間傳送和展示資料的載體,具體的解釋可參考上面內容,限制:1)僅包含最少的用于傳輸的資訊,不要使用一個物件包含所有的欄位即萬能物件;2)不可以從資料模型繼承(這個問題尤其普遍),可使用工具實作與資料模型的互轉;3)是包、服務間傳輸資訊的唯一載體;4)不得包含業務邏輯,下面給出了一個“資料字典”視圖模型的示例,請參考,
@ApiModel(value = "https://www.cnblogs.com/skevin/archive/2022/03/07/資料字典資訊") public class DictionaryVO extends VOBase { private static final int MAX_VALUE_LENGTH = 32; @ApiModelProperty(value = "字典值,長度:32", required = true) private String value; @Override public ParameterValidationResult validate() { if (this.classId == null) { return ParameterValidationResult.failed(OperationMessages.INVALID_CLASS_ID); } if (StringUtils.isEmpty(this.value) || this.value.length() > MAX_VALUE_LENGTH) { return ParameterValidationResult.failed(String.format(OperationMessages.INVALID_VALUE_LENGTH, MAX_VALUE_LENGTH)); } return super.validate(); } public static DictionaryVO of(DictionaryDataEntity entity) { if (entity == null) { return null; } DictionaryVO vo = new DictionaryVO(); BeanUtils.copyProperties(entity, vo); return vo; } }
上述代碼中,視圖模型繼承于“VOBase”自定義類,此類包含了“validate”方法用于對視圖模型中的資訊進行驗證,切記:前端、其它服務和包傳過來的資訊永遠是不可信的,通過在視圖模型中增加驗證邏輯可以讓代碼更簡潔,“of”方法用于資料模型和視圖模型的轉換,“ApiModel”引入了“Swagger”用于對欄位進行說明,
3、領域模型
定義:描述業務物體屬性和行為的模型,一般來說是充血模型,后續會細講,限制:1)不得依賴于架構中的其它層、第三方基礎框架如Spring、DAO、HTTPClinet等,這么說吧,除了JDK外其它都不能依賴,就和狗一樣,依賴少表示血統純粹,越純粹越好;2)作用范圍只能在業務模型層中,不得外泄;3)嚴格注意每個模型的訪問限制,能不用public就不用,4)最好自行寫一些包含了公共能力(比如“物件判等”)的領域模型基類來保證代碼的干凈,
這個里面我覺得有可能爭議最大的是關于領域模型的依賴,有一些比如字串工具、日期工具這種的第三方類別庫其實很普遍,完全的不依賴是否會更加的極端?怎么說呢,我個人在使用ODD開發的時候寫了一套基礎組件,包含了驗證、值型別、物體型別、領域倉庫、Saga等領域模型相關的組件(大部分都是抽象類)和少量的工具類,需要什么拿來即用,我稱之為“通用能力庫”,這個庫本身是自己寫的(注:本系列文章只關注DDD知識的講解,不會推薦任何的、成熟度不夠高的尤其是標榜為DDD的框架),也的確沒用到JDK外的其它第三方組件,而且,領域模型本身專用于業務邏輯處理和計算,像什么字串格式化啊、日期格式化啊其實就不應該在領域模型里搞,寫通用能力庫畢竟也會占用精力,我的推薦是您在領域模型中盡量少的依賴第三方組件,越少越好,有些書籍中會展示在領域模型中注入DAO來實作嵌套物件的“懶加載”,我個人認為這個是不化不類,只有在需要向性能妥協時才會使用,
總結
本章講了三個模型,雖然內容不多,可真正使用起來也是有一定的要求的,軟體開發是個細活兒,想做好當然要負出精力了,您完全可以突破上述所提到的限制,也能出東西,可需求總是變化的,總得改代碼,到時候吃虧的還是自己,這沒辦法,坑兒是您自己給自己挖的,除非您寫完了代碼就打算跑路,那也給了后面接手的人罵爹的機會,另外,您可能認為本章和DDD沒關系,我得提前宣告:咱不是寫小說呢,一個字多少多少錢,這些經驗您還真在其它書上找不著,而且,越是細節越能體現開發者的能力,搞技術到后面比的是什么?不再是會什么不會什么了,而是比誰開發的質量高和可維護性高,兩個員工做同樣的需求尤其是功能增強性相關的,一個5分鐘搞定,一個3天,你是老板你用哪個 ?
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/439186.html
標籤:其他
