POJO分层领域模型
POJO分層領(lǐng)域模型
前言
當(dāng)你百度一下 po,jo,dto,vo,bo,entity,model,于是,網(wǎng)上出現(xiàn)這樣,啊,呸!臘雞,沒(méi)一個(gè)說(shuō)的明白的,要么就是拉一個(gè)阿里巴巴的規(guī)范,要么就是光有概念,沒(méi)有實(shí)際,或者說(shuō)得云里霧里,對(duì)于看到本文的各位 極客,不妨來(lái)跟我一探究竟吧!
首先,像阿里巴巴這種級(jí)別的公司,和其他的中小型公司,不可相提并論,所謂最好的架構(gòu),不是什么 消息隊(duì)列,緩存都往上堆的,要適合當(dāng)前項(xiàng)目的才是最好的架構(gòu),是吧。所以本文為了能夠給各位同學(xué)講解清晰,先從簡(jiǎn)單的,常見(jiàn)的開(kāi)始吧!
本文是按照,前后端分離,講解,不提供類似 JSP,Thymeleaf 模板引擎 這種不分離的講解。
會(huì)設(shè)計(jì)到 SOA 面向服務(wù)架構(gòu),SpringCloud 微服務(wù)架構(gòu),最好有一定的開(kāi)發(fā)經(jīng)驗(yàn)
阿里巴巴規(guī)約
由于阿里巴巴的規(guī)范被廣泛流傳,顯然,很多人被規(guī)范中的 manger,vo,query 搞得暈頭轉(zhuǎn)向
之前咱不是說(shuō)了嗎,最適用的才是最好的架構(gòu),我也想住大別墅啊,可以工資不允許啊。我有裝修別墅的圖紙,你有別墅嗎?
別急,后面會(huì)帶你慢慢看懂這幾層架構(gòu),和上面分層規(guī)約的。看的時(shí)候最好把這玩意畫在紙上,或者截圖哦。
常見(jiàn)的 O
當(dāng)然,這里不僅僅是收集了 阿里巴巴規(guī)約上的 Do,Dto ,Bo,Query,Vo,還有其他地方的規(guī)范,以及各種 DAO,PO,POJO,Model,Entity,啊啊啊,我要崩潰了,怎么這么多啊,亂七八糟的。
O 指的是 Object,也就是對(duì)象,面向?qū)ο蟮耐瑢W(xué)應(yīng)該都不陌生。
| POJO | Plan Ordinary Java Object | 普通的 Java 對(duì)象 | 瞧瞧這名字,多普通啊 |
| Dao | Data Access Object | 數(shù)據(jù)訪問(wèn)對(duì)象 | 這其實(shí)特別容易區(qū)別,下面的文章待會(huì)講 |
| Dto | Data Transfer Object | 數(shù)據(jù)傳輸對(duì)象 | 有點(diǎn)繞了,啥玩意叫 Transfer 傳輸啊,這有歷史錯(cuò)誤 |
| Po | Persistant Object | 持久化對(duì)象 | 哦豁,阿里巴巴規(guī)范可沒(méi)這玩意啊 |
| Do | Data Object | 數(shù)據(jù)對(duì)象 | |
| Bo | Business Object | 業(yè)務(wù)隊(duì)對(duì)象 | 嗐,業(yè)務(wù)掛鉤的唄 |
| Ao | Application Object | 應(yīng)用對(duì)象 | 應(yīng)用?怎么感覺(jué)和 業(yè)務(wù) Bo 差不多呢 |
| Vo | Value Object / View Object | 值對(duì)象 / 視圖對(duì)象 | 啥,還有2種解釋? |
| Query | Query | 查詢 | 咋混個(gè)這玩意進(jìn)來(lái)? |
| Model | Model | 模型 | 啥玩意是模型, |
| Entity | Entity | 實(shí)體 | 啥玩意是實(shí)體, |
| Bean | Bean | bean 對(duì)象 | 啥玩意是bean, |
如何使用
點(diǎn)我查看在線POJO 分層模型
三層架構(gòu)
那么先從最簡(jiǎn)單的 Controller,Service,Dao 入手。這是最經(jīng)典的三層架構(gòu)。
這里的前端,不一定是指瀏覽器,也有可能是 app,小程序,不管是 B/S 架構(gòu),還是 C/S 架構(gòu),我們后端如果使用 Spring,還是 SpringBoot,還是 SpringCloud,下面的這張圖,應(yīng)當(dāng)是很熟悉的。
Controller
以用戶的增刪改查為例
- 新增
- 這種情況一般是會(huì)攜帶表單數(shù)據(jù)提交上來(lái)的,比如 用戶昵稱,手機(jī)號(hào),性別等。那我們用什么 O 呢?,別急,先看看哪里會(huì)用到對(duì)象
- 查詢
- 批量查詢
- 在批量查詢列表的時(shí)候,查詢也是攜帶一些查詢條件,比如性別,姓名模糊查詢,分頁(yè)的page 和 rows 等
- 額外吐槽一句,為啥分頁(yè)會(huì)有人用 pageSize 和 pageNum 啊????強(qiáng)烈推薦用 page 和 rows 啊!!!!
- 那么用什么 O 來(lái)封裝這些 姓名,模糊查詢,日期呢?
- 單條查詢
- 一般單條信息的查詢會(huì)使用 主鍵id,可能會(huì)寫在 請(qǐng)求路徑中
- 批量查詢
- 修改
- 修改的時(shí)候一般會(huì)根據(jù)主鍵去修改,然后攜帶最新的表單數(shù)據(jù),新的昵稱,新的家庭住址,用什么 O 呢?
- 刪除
- 單條刪除的話,一般使用主鍵id 就可以了,當(dāng)然很多情況下是邏輯刪除,并不是真刪
- 批量刪除的話,就需要一個(gè)列表來(lái)接收了。用什么 O 呢?,這種情況一般只需要這個(gè)樣子就可以了,并不需要額外的對(duì)象封裝
來(lái)看看 阿里巴巴規(guī)約
-
從網(wǎng)頁(yè),APP 前端傳輸過(guò)來(lái),進(jìn)入到 Controller 層,用什么接收
-
新增和修改,使用 DTO
-
查詢使用 Query
-
理由
- DTO 被翻譯成 Data Transfer Object 數(shù)據(jù)傳輸對(duì)象本身就是錯(cuò)誤的transfer 應(yīng)當(dāng)被翻譯成 轉(zhuǎn)移transport 才是真正的 傳輸HTTPS 超文本傳輸協(xié)議,這本身就是歷史理由問(wèn)題,可以參考 https://baike.baidu.com/item/超文本轉(zhuǎn)移協(xié)議/1675080?fr=aladdin在 ISO 七層網(wǎng)絡(luò)模型中應(yīng)用層 HTTPS 應(yīng)當(dāng)是轉(zhuǎn)移,但是由于歷史翻譯原因表示層會(huì)話層傳輸層 TCP/UDP 指 端到端 (ip:port 到 ip:port) 傳輸比特網(wǎng)絡(luò)層數(shù)字鏈路層物理層
-
由于歷史悠久,錯(cuò)誤的就當(dāng)成正確的吧,你總不能天天喊著,中國(guó)的國(guó)寶是 貓熊 吧!那么我們就把 DTO 當(dāng)成 數(shù)據(jù)傳輸對(duì)象
-
前端 到 后端符合傳輸?shù)恼Z(yǔ)義,而且新增和修改的數(shù)據(jù)模型一般是一致的,新增的用戶信息有多少,修改的字段一般也是大致相同的
-
爭(zhēng)議
- 查詢使用 Query 還是 Dto,博主本人更傾向 查詢使用 Query
- 因?yàn)椴樵?和 新增修改本身字段就不同,而且 Query 更符合查詢的語(yǔ)義,分頁(yè)查詢一般提取父類 page 和 rows
- 如果使用 Dto 來(lái)接收數(shù)據(jù),新增用戶是 UserDto,查詢用戶的查詢字段也是 UserDto,命名容易沖突。
-
-
從到 Controller 層返回給前端,用什么
- 返回使用 Vo
- 理由
- VO 有的地方當(dāng)做 Value Object,有的地方規(guī)范當(dāng)做 View Object,博主更傾向于 View Object
- 根據(jù)上述傳輸使用 DTO,如果從后端向前端也使用 DTO,那么會(huì)造成 DTO 命名沖突,傳進(jìn)來(lái)是 UserDto,傳出去也是 UserDto
- 那么兩個(gè) Dto 能夠復(fù)用嗎?不行,因?yàn)閭鬟M(jìn)來(lái)的可能有很多字段,而返回出去的可能并不需要那么多字段,部分敏感信息不應(yīng)當(dāng)返回。
- DTO,Query,Vo,一看就知道,哪個(gè)是干嘛的?
- 返回使用 Vo
Dao
Dao Data Access Object 數(shù)據(jù)訪問(wèn)對(duì)象 ,這個(gè)一般情況是 Mybatis 的接口層,比如 UserDao。UserMapper 等
如果使用像 SpringDataJPA 這種,這一層的命名可能是 UserReposritory,
Dao 其實(shí)就不應(yīng)該拿到 pojo 分層領(lǐng)域模型來(lái)講,因?yàn)橹v的根本不是一個(gè)東西。
眾所周知,在國(guó)內(nèi)是以 Mybatis 占主流,而全局開(kāi)發(fā)者調(diào)查中,SpringDataJPA 的市場(chǎng)占有份額也極高,這就是 ORM 的區(qū)別
以 Mybaits 為首的半自動(dòng)映射,和 hibernate,SpringDataJPA 全自動(dòng)映射,的兩種策略來(lái)說(shuō)明
Po Persistant Object 持久化對(duì)象,這個(gè)說(shuō)法常常見(jiàn)于 Mybatis,SpringDatJPA,MybatisPlus 這些持久層框架的文檔中,就是 Java 對(duì)象與數(shù)據(jù)庫(kù)表的對(duì)應(yīng)
Do Data Object 數(shù)據(jù)對(duì)象,阿里巴巴開(kāi)發(fā)規(guī)范,此對(duì)象與數(shù)據(jù)庫(kù)表結(jié)構(gòu)一一對(duì)象
這2個(gè)也是爭(zhēng)議極大的。
博主根據(jù)使用 Mybatis 和 SpringDataJPA 操作 MySQL 的經(jīng)驗(yàn)來(lái)看,更傾向于 全自動(dòng)映射使用 PO,半自動(dòng)映射使用 DO,包括像 MongoDB 類似的非關(guān)系型數(shù)據(jù)庫(kù)也是如此。
Java 中一個(gè) User 對(duì)象有 10 個(gè)屬性,數(shù)據(jù)庫(kù)表中 sys_user 有10個(gè)字段,這種完全一一對(duì)應(yīng)的。下文稱為“完全映射”
以 Mybatis 舉例,在多表關(guān)聯(lián)查詢的時(shí)候,從3張表中分別抽取2個(gè)字段,組合成新的一個(gè)對(duì)象,這種對(duì)象是無(wú)法 完全映射的。屬于臨時(shí)組合出來(lái)的對(duì)象。
以 SpringDataJPA 舉例,使用 @OneToMany @ManyToMany 仍然可以通過(guò)對(duì)象的完全映射到數(shù)據(jù)庫(kù)表
Do 還可以用作領(lǐng)域驅(qū)動(dòng)的對(duì)象。后文再講。這里略過(guò)。
Service
以增刪改為例
從 Controller 層,傳給 Service 層,仍然是 UserDto,UserQuery。但是這一層是需要進(jìn)行處理的。不可以直接與數(shù)據(jù)庫(kù)交互
比如 Dto 傳過(guò)來(lái)的時(shí)候,沒(méi)有 狀態(tài),沒(méi)有創(chuàng)建時(shí)間,沒(méi)有創(chuàng)建人,而且如果有設(shè)計(jì)不規(guī)范的時(shí)候,比如 Dto 里是一個(gè)列表,但是數(shù)據(jù)庫(kù)是一個(gè)字符串
需要將 將 dto 里的 carIds 轉(zhuǎn)成 字符串,再塞入到 po 里,所以 service 里可能像下面這樣寫。
比如 Query 里的查詢條件是字典值,傳過(guò)來(lái)的是男,我們要轉(zhuǎn)成 1,再去 數(shù)據(jù)庫(kù)查詢
service 中的查詢,從數(shù)據(jù)庫(kù)查詢出來(lái)的 Po,或者 Do,也是不可以直接返回出去的,需要將一些數(shù)據(jù)進(jìn)行處理,比如將密碼置為 * 號(hào),比如文件大小 10240 轉(zhuǎn)成 10MB,圖中的創(chuàng)建時(shí)間其實(shí)可以在 vo 里用@JsonFormat 設(shè)置,或者框架統(tǒng)一設(shè)置,這里用的老圖,
Bo 里可以隨便添加字段,可以當(dāng)成一個(gè)中間處理的對(duì)象,比如 原本文件大小的字段叫 fileSize 是數(shù)值型的,可以在 bo 里加一個(gè) fileSizeHuman ,字符串類型的,然后 vo 里使用 fileSizeHuman來(lái)接收,一般都使用統(tǒng)一的 bean 轉(zhuǎn)換工具類。
這就是 service 層
SOA 架構(gòu)
SOA 架構(gòu),以 dubbo + zookeeper 為代表的 SOA 面向服務(wù)的架構(gòu)風(fēng)格,controller 和 service 部署在不同的機(jī)器上,這時(shí)候需要注冊(cè)到 注冊(cè)中心上去,
就會(huì)需要對(duì)象序列化,就會(huì)有 對(duì)象傳輸 的場(chǎng)景,
那么在這種情況之下,對(duì)象傳輸就不是直接返回給 controller,而是有一個(gè)網(wǎng)絡(luò)傳輸?shù)倪^(guò)程,可以使用 Dto 來(lái)命名。
微服務(wù)架構(gòu)
不過(guò)現(xiàn)在都是微服務(wù)架構(gòu),SOA 架構(gòu)的風(fēng)格的系統(tǒng) 沒(méi)那么多,所以上面 service 或 manager 向外傳輸 Dto 的情況也就沒(méi)那么多
因?yàn)镾OA架構(gòu)風(fēng)格,需要對(duì)外暴露統(tǒng)一接口,所以很多框架還會(huì)有 Service 層的接口,然后再有一個(gè)實(shí)現(xiàn)類,
但是在微服務(wù)盛行的今天,仍然有很多項(xiàng)目在 service 層分出一層接口層,雖然是有好處,博主個(gè)人認(rèn)為完全沒(méi)有必要。在比較特定的場(chǎng)景,可以單獨(dú)加,而且這些特定的場(chǎng)景是可以通過(guò)其他手段規(guī)避的。
DDD 領(lǐng)域驅(qū)動(dòng)
領(lǐng)域驅(qū)動(dòng),先簡(jiǎn)單的科普下,建議先網(wǎng)上搜一搜領(lǐng)域驅(qū)動(dòng)的概念,再結(jié)合一起看,以之前的 三層架構(gòu)來(lái)舉例,
一個(gè) UserPo 只對(duì)應(yīng)一張 sys_user 表,一個(gè) User 有 3 個(gè)訂單,訂單對(duì)應(yīng) 訂單表 sys_order 表,想要查詢用戶和訂單,或者一個(gè)用戶下3個(gè)訂單
寫個(gè)偽代碼
public class UserService{@Resourceprivate UserDao userDao;@Resourceprivate OrderDao orderDao;public void insertUser(UserDto userDto){//插入用戶信息//插入訂單信息}public UserInfo select(UserQuery userQuery){//查詢用戶信息//查詢訂單信息//訂單信息列表 set 到 userInfo 的字段里去}}以上的代碼都是在 service 業(yè)務(wù)邏輯層進(jìn)行操作。
而領(lǐng)域驅(qū)動(dòng)的代碼可能是下面
public class UserService{public void insertUser(UserDto userDto){// UserDto 轉(zhuǎn)成 UserDouserDo.insert();}public UserInfo select(UserQuery userQuery){//UserQuery 轉(zhuǎn)成 UserDoUserInfo userInfo = userDo.info();}} public class UserDo{@Resourceprivate UserDao userDao;@Resourceprivate OrderDao orderDao;public void insert(UserDto userDto){//插入用戶信息//插入訂單信息}public UserInfo info(UserQuery userQuery){//查詢用戶信息//查詢訂單信息//訂單信息列表 set 到 userInfo 的字段里去}}將緊密的業(yè)務(wù)邏輯,放到業(yè)務(wù)模型中去,讓模型成為一個(gè)充血模型,
有人可能感覺(jué)到,這有啥區(qū)別,不就是從一個(gè)類,移動(dòng)到例外一個(gè)類里去寫嘛,一是我上面的例子的確不是非常好,二,我舉的例子是和交通方面相關(guān),各位可能也聽(tīng)不明白細(xì)節(jié)。
一般的領(lǐng)域驅(qū)動(dòng),都是用于專業(yè)性更強(qiáng)的,更加緊密的業(yè)務(wù)模型。
比如一個(gè)電腦,你可能拆成 n 多張表。顯示器表,電源表,網(wǎng)卡表,,,,, 30 張表,之前的是將業(yè)務(wù)邏輯放到 service 層去寫,相當(dāng)于我們程序員知道如何拆卸,組裝電腦,需要哪些顯示器,電源,但是如果這個(gè)程序員并不是非常熟悉電腦呢?
我們現(xiàn)在用一個(gè)類,Computer 類,這個(gè)領(lǐng)域模型里,就直接包含了,組裝方法,拆卸方法,用領(lǐng)域模型來(lái)驅(qū)動(dòng) 整個(gè)項(xiàng)目的 CRUD。
再以保險(xiǎn)距離,保險(xiǎn)案例摘抄網(wǎng)上 DDD 驅(qū)動(dòng)講解。
以保險(xiǎn)業(yè)務(wù)為例來(lái)進(jìn)行編程實(shí)踐,一個(gè)高度抽象的保險(xiǎn)領(lǐng)域劃分如圖所示。通過(guò)用例分析,我們把整個(gè)業(yè)務(wù)劃分成產(chǎn)品域、承保、核保、理賠等多個(gè)領(lǐng)域(Bounded-Context),每個(gè)領(lǐng)域又可以根據(jù)業(yè)務(wù)發(fā)展情況拆分子域。當(dāng)然,完備保險(xiǎn)業(yè)務(wù)要比圖中展現(xiàn)的復(fù)雜太多。
領(lǐng)域驅(qū)動(dòng),需要一個(gè)專業(yè)性比較強(qiáng)的專家,結(jié)合程序員,才能比較好的切實(shí)落地,博主曾經(jīng)在智慧交通系統(tǒng)上應(yīng)用該思想,組內(nèi)開(kāi)發(fā)人員不太了解該理念,另外和交通的專家溝通也有缺陷,并不是很成功,效果也并不好,直到項(xiàng)目中期才逐漸好起來(lái),由于整體架構(gòu)和存量代碼的問(wèn)題,并不能完全的稱為 領(lǐng)域驅(qū)動(dòng)。
不過(guò)可以將該領(lǐng)域驅(qū)動(dòng)的思想,應(yīng)用于其他地方。而這個(gè)領(lǐng)域?qū)ο?#xff0c;博主更傾向于 DO 來(lái)命名。當(dāng)然,這里也設(shè)計(jì) 應(yīng)用層領(lǐng)域 AO,和 數(shù)據(jù)模型 Model 等
小結(jié)
鑒于領(lǐng)域驅(qū)動(dòng),博主只在兩個(gè)系統(tǒng)上應(yīng)用,并不能深切的進(jìn)行體會(huì),希望能夠和各位同學(xué)交流,共同進(jìn)步。
圖中根據(jù)博主的個(gè)人理解,描述了 DTO,Query,Vo,Bo,Po,Do 等,還有其他的一些
以 SpringBoot 的 配置文件參數(shù)注入為例,常見(jiàn)2種方式注入,1是如下的 @Value
2是 @ConfigurationProperties(“alioss”) 對(duì)象注入。
兩種方式雖然都可以進(jìn)行注入,但是博主更喜歡第二種,因?yàn)榭梢栽陬惱镞M(jìn)行 bean 的引用,如下圖,
如果是配置文件中的 字符串還是經(jīng)過(guò) base64 加密的,也可以在 @ConfigurationProperties(“alioss”) 這種方式的 get 方法里,進(jìn)行轉(zhuǎn)化,
如果配置文件中的 名稱 accessKey 改變成 accessKeyName,我們也可以在第二種方式中,提供另外一個(gè)字段 accessKeyName,但是不提供 set get,然后對(duì) accessKey 的 get 方法進(jìn)行修改,也不會(huì)改變項(xiàng)目里已經(jīng)調(diào)用的字段。相當(dāng)于中間加了一層緩沖層
那么這種緩沖層其實(shí)也并不好歸屬 DTO,Query,Vo,Bo,Po,Do,博主常用 Model,或者 Entity 描述該配置類,
如果我們網(wǎng)站上,有一個(gè)查詢手機(jī)號(hào)歸屬地的功能,用戶從網(wǎng)頁(yè)請(qǐng)求,但是我們系統(tǒng)后臺(tái)是請(qǐng)求第三方接口的, 那么我們?cè)跇I(yè)務(wù) service 層之后還可以加一層 通用處理層,這時(shí)候可能就涉及到網(wǎng)絡(luò)請(qǐng)求,又可能涉及到 請(qǐng)求的數(shù)據(jù),和響應(yīng)的數(shù)據(jù),我們也會(huì)使用一個(gè)對(duì)象來(lái)封裝,
那么這種請(qǐng)求對(duì)象或者響應(yīng)對(duì)象,其實(shí)也并不好歸屬 DTO,Query,Vo,Bo,Po,Do。 當(dāng)然也可以使用 Dto 來(lái)命名,但是這就比較容易混淆了嘛。
博主也常用 Model,Entity,來(lái)命名,比如 xxxReqModel,xxxRespModel。
而對(duì)于這些所有的,pojo 是一個(gè)很好的概述,不是嗎?Plan Ordinary Java Object POJO
| POJO | Plan Ordinary Java Object | 普通的 Java 對(duì)象 | 瞧瞧這名字,多普通啊 |
| Dao | Data Access Object | 數(shù)據(jù)訪問(wèn)對(duì)象 | 這其實(shí)特別容易區(qū)別,下面的文章待會(huì)講 |
| Dto | Data Transfer Object | 數(shù)據(jù)傳輸對(duì)象 | 有點(diǎn)繞了,啥玩意叫 Transfer 傳輸啊,這有歷史錯(cuò)誤 |
| Po | Persistant Object | 持久化對(duì)象 | 哦豁,阿里巴巴規(guī)范可沒(méi)這玩意啊 |
| Do | Data Object | 數(shù)據(jù)對(duì)象 | |
| Bo | Business Object | 業(yè)務(wù)隊(duì)對(duì)象 | 嗐,業(yè)務(wù)掛鉤的唄 |
| Ao | Application Object | 應(yīng)用對(duì)象 | 應(yīng)用?怎么感覺(jué)和 業(yè)務(wù) Bo 差不多呢 |
| Vo | Value Object / View Object | 值對(duì)象 / 視圖對(duì)象 | 啥,還有2種解釋? |
| Query | Query | 查詢 | 咋混個(gè)這玩意進(jìn)來(lái)? |
| Model | Model | 模型 | 啥玩意是模型, |
| Entity | Entity | 實(shí)體 | 啥玩意是實(shí)體, |
| Bean | Bean | bean 對(duì)象 | 啥玩意是bean, |
Bean 的話,博主更傾向于, JavaBean,不要比 Bean 來(lái)命名。
碼字不易,點(diǎn)點(diǎn)小手。
總結(jié)
以上是生活随笔為你收集整理的POJO分层领域模型的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: fNIRS | 近红外功能成像技术基本原
- 下一篇: 【无机纳米材料科研制图——OriginL