偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

從 MVC 到 DDD 的架構(gòu)演進(jìn)

開(kāi)發(fā) 架構(gòu)
本文從 MVC 架構(gòu)開(kāi)始講述了如何從演進(jìn)到 DDD 架構(gòu),限于篇幅很多 DDD 的知識(shí)點(diǎn)沒(méi)有講到,希望大家在實(shí)踐過(guò)程中能靈活運(yùn)用,盡享 DDD 給業(yè)務(wù)帶來(lái)的價(jià)值。

DDD 這幾年越來(lái)越火,資料也很多,大部分的資料都偏向于理論介紹,有給出的代碼與傳統(tǒng) MVC 的三層架構(gòu)差異較大,再加上大量的新概念很容易讓初學(xué)者望而卻步。本文從 MVC 架構(gòu)角度來(lái)講解如何演進(jìn)到 DDD 架構(gòu)。

從 DDD 的角度看 MVC 架構(gòu)的問(wèn)題

代碼角度:

  • 瘦實(shí)體模型:只起到數(shù)據(jù)類的作用,業(yè)務(wù)邏輯散落到 service,可維護(hù)性越來(lái)越差;
  • 面向數(shù)據(jù)庫(kù)表編程,而非模型編程;
  • 實(shí)體類之間的關(guān)系是復(fù)雜的網(wǎng)狀結(jié)構(gòu),成為大泥球,牽一發(fā)而動(dòng)全身,導(dǎo)致不敢輕易改代碼;
  • service 類承接的所有的業(yè)務(wù)邏輯,越來(lái)越臃腫,很容易出現(xiàn)幾千行的 service 類;
  • 對(duì)外接口直接暴露實(shí)體模型,導(dǎo)致不必要開(kāi)放內(nèi)部邏輯對(duì)外暴露,就算有 DTO 類一般也是實(shí)體類的直接 copy;
  • 外部依賴層直接從 service 層調(diào)用,字段轉(zhuǎn)換、異常處理大量充斥在 service 方法中;

項(xiàng)目管理角度:

  • 交付效率:越來(lái)越低;
  • 穩(wěn)定性差:不好測(cè)試,代碼改動(dòng)的影響范圍不好預(yù)估;
  • 理解成本高:新成員介入成本高,長(zhǎng)期會(huì)導(dǎo)致模塊只有一個(gè)人最熟悉,離職成本很大;

第一層:初出茅廬

以上的問(wèn)題越來(lái)越嚴(yán)重,很多人開(kāi)始把眼光轉(zhuǎn)向 DDD,于是埋頭啃了幾本大部頭的書(shū),對(duì)以下概念有了基本的了解:

  • 統(tǒng)一語(yǔ)言
  • 限界上下文
  • 領(lǐng)域、子域、支撐域
  • 聚合、實(shí)體、值對(duì)象
  • 分層:用戶接口層、應(yīng)用層、領(lǐng)域?qū)?、基礎(chǔ)層

于是把 MVC 架構(gòu)進(jìn)行了改造,演進(jìn)成 DDD 的分層架構(gòu)。

DDD 分層架構(gòu):

image

MVC 架構(gòu)到 DDD 分層架構(gòu)的映射:

image

至此,算了基本入門了 DDD 架構(gòu),擴(kuò)展性也得到了一定的提升。不過(guò)隨著業(yè)務(wù)的發(fā)展,不斷冒出新的問(wèn)題:

  • 一段業(yè)務(wù)邏輯代碼,到底應(yīng)該放到應(yīng)用層還是領(lǐng)域?qū)樱?/li>
  • 領(lǐng)域服務(wù)當(dāng)成原來(lái)的 MVC 中的 service 層,隨著業(yè)務(wù)不斷發(fā)展,類也在不斷膨脹,好像還是老樣子?。?/li>
  • 聚合包含多個(gè)實(shí)體類,這個(gè)接口用不到這么多實(shí)體,為了性能還是直接寫(xiě)個(gè) SQL 返回必要的操作吧,不過(guò)這樣貌似又回到了 MVC 模式
  • 既然實(shí)體類可以包含業(yè)務(wù)邏輯、領(lǐng)域服務(wù)也可以放業(yè)務(wù)邏輯,那到底放哪里?
  • 資料上說(shuō)領(lǐng)域?qū)硬荒苡型獠恳蕾?,要做?100% 單測(cè)覆蓋,可是我的領(lǐng)域服務(wù)中需要用到外部接口、中央緩存等等,那這不就有了外部依賴了嗎?

第二層:草船借箭(戰(zhàn)術(shù)設(shè)計(jì))

帶著問(wèn)題不斷學(xué)習(xí)他人經(jīng)驗(yàn),并不斷的嘗試,逐漸 get 到以下技能:

1. 領(lǐng)域?qū)?/h4>

領(lǐng)域(domain)是個(gè)模塊,包含以下組成部分,傳統(tǒng)的 service 按功能可能拆分到任何一個(gè)地方,各司其職。

  • 1 個(gè)聚合
  • 1 到多個(gè)實(shí)體
  • 若干值對(duì)象
  • 多個(gè) DomainService
  • 1 個(gè) Factory:新建聚合
  • 1 個(gè) Repository:聚合倉(cāng)儲(chǔ)服務(wù)

(1) 聚合根(AggregateRoot)

聚合本身也是一個(gè)實(shí)體,聚合可以包含其他實(shí)體,其他實(shí)體不能脫離聚合而單獨(dú)提供服務(wù),比如一篇文章下的評(píng)論,評(píng)論必須從屬與文章,沒(méi)有文章也就沒(méi)有評(píng)論。倉(cāng)庫(kù)層(repository)也必須是以聚合為核心提供服務(wù)的;

  • 實(shí)體:可以理解為一張數(shù)據(jù)庫(kù)表,必須有主鍵;
  • 值對(duì)象:沒(méi)有主鍵,依附于實(shí)體而存在,比如用戶實(shí)體下住址對(duì)象,一般在數(shù)據(jù)庫(kù)中已 json 字符串的形式存在;最常見(jiàn)的值對(duì)象是枚舉;

(2) 倉(cāng)庫(kù)服務(wù)(repository)

資源庫(kù)是聚合的倉(cāng)儲(chǔ)機(jī)制,外部世界通過(guò)資源庫(kù),而且只能通過(guò)資源庫(kù)來(lái)完成對(duì)聚合的訪問(wèn)。資源庫(kù)以聚合的整體管理對(duì)象。因此,一個(gè)聚合只能有一個(gè)資源庫(kù)對(duì)象,那就是以聚合根命名的資源庫(kù)。除此之外的其他對(duì)象,都不應(yīng)該提供資源庫(kù)對(duì)象。倉(cāng)儲(chǔ)服務(wù)的實(shí)現(xiàn)一般有 Spring Data JPA、Mybatis 兩種方式。

如果是用 Spring Data JPA 實(shí)現(xiàn),直接使用 JPA 注解 @OneToOne、@OneToMany,配合 fetch 配置,即可一個(gè)方法查詢出所有的關(guān)聯(lián)實(shí)體。

如果是用 Mybatis 實(shí)現(xiàn),那么 repository 需要加入多個(gè) mapper 的引用,再手動(dòng)做拼裝。

這里有一個(gè)經(jīng)典的 Hibernate 笛卡爾積問(wèn)題,答案是在聚合根中,一般不會(huì)加在大量的關(guān)聯(lián)實(shí)體對(duì)象。如果確實(shí)需要查詢關(guān)聯(lián)對(duì)象而關(guān)聯(lián)對(duì)象又比較多怎么辦呢?在 DDD 中有一個(gè) CQRS (Command-Query Responsibility Segregation) 模式,是一種讀寫(xiě)分離模式,在此場(chǎng)景中需要將查詢操作放到查詢命令中分頁(yè)查詢。

當(dāng)然 CQRS 也是一個(gè)很復(fù)雜模式,不應(yīng)照搬他人方案,而是根據(jù)自己的業(yè)務(wù)場(chǎng)景選擇適合自己的方案,以下列舉了 CQRS 的幾種應(yīng)用模式:

image

(3) 工廠服務(wù)(factory)

作用是創(chuàng)建聚合,只傳入必要的參數(shù),工廠服務(wù)內(nèi)部隱藏復(fù)雜的創(chuàng)建邏輯。簡(jiǎn)單的聚合可以直接通過(guò) new、靜態(tài)方法等創(chuàng)建,不是必須由 factory 創(chuàng)建。

(4) 領(lǐng)域服務(wù)

單個(gè)實(shí)體對(duì)象能處理的邏輯放到實(shí)體里,多個(gè)實(shí)體或有交互的場(chǎng)景放到領(lǐng)域服務(wù)里。

領(lǐng)域服務(wù)可不可以調(diào)用倉(cāng)儲(chǔ)層或外部接口? 可以,但不能直接和領(lǐng)域服務(wù)代碼放一起,領(lǐng)域服務(wù)模塊存放 API,實(shí)現(xiàn)放基礎(chǔ)層(infrastructure)。

領(lǐng)域服務(wù)對(duì)象不建議直接以聚合名 + DomainService 命名,而要以操作命令關(guān)聯(lián),比如用戶保存服務(wù)命名為:UserSaveService, 審核服務(wù):UserAuditSerivce。

2. 應(yīng)用層

應(yīng)用層通過(guò)應(yīng)用服務(wù)接口來(lái)暴露系統(tǒng)的全部功能。在應(yīng)用服務(wù)的實(shí)現(xiàn)中,它負(fù)責(zé)編排和轉(zhuǎn)發(fā),它將要實(shí)現(xiàn)的功能委托給一個(gè)或多個(gè)領(lǐng)域?qū)ο髞?lái)實(shí)現(xiàn),它本身只負(fù)責(zé)處理業(yè)務(wù)用例的執(zhí)行順序以及結(jié)果的拼裝。通過(guò)這樣一種方式,它隱藏了領(lǐng)域?qū)拥膹?fù)雜性及其內(nèi)部實(shí)現(xiàn)機(jī)制。

比如下訂單服務(wù)的方法:

public void submitOrder(Long orderId) {
Order order = OrderFetchService.fetchById(orderId); //獲取訂單對(duì)象
OrderCheckSerivce.check(order); //驗(yàn)證訂單是否有效
OrderSubmitSerivce.submit(order); //提交訂單
ShoppingCartClearService.clear(order); //移除購(gòu)物車中已購(gòu)商品
NotifySerivce.emailNotify(order.getUser()); //發(fā)送郵件通知買家
}

對(duì)于復(fù)雜的業(yè)務(wù)來(lái)說(shuō),應(yīng)用層也有幾種模式:

  • 編排服務(wù):最典型比如 Drools;
  • Command、Query 命令模式;
  • 業(yè)務(wù)按 Rhase、Step 逐層拆分模式;

image

3. Maven 模塊劃分

基礎(chǔ)層是比較簡(jiǎn)單一層,不過(guò)這里還有個(gè)比較疑惑的問(wèn)題:按照 DDD 的四層架構(gòu)圖去劃分 Maven 模塊,基礎(chǔ)層是最上的一層,但是基礎(chǔ)層也要包含基礎(chǔ)組件供其他層使用,這時(shí)基礎(chǔ)層應(yīng)該是放到最下層,直接按照這樣構(gòu)建 Maven 模塊會(huì)造成循環(huán)依賴。

image

相比來(lái)說(shuō),另一個(gè)架構(gòu)圖更準(zhǔn)確一些,不過(guò)依然沒(méi)有直觀體現(xiàn) Maven 模塊如何劃分。

image

我的最佳實(shí)踐是將基礎(chǔ)層拆分兩部分,一部分是基礎(chǔ)的組件 + 倉(cāng)儲(chǔ) API,一部分是實(shí)現(xiàn),maven 模塊劃分圖如下所示:

image

第三層:運(yùn)籌帷幄(戰(zhàn)略設(shè)計(jì))

經(jīng)過(guò)以上的兩層的磨煉,恭喜你把 DDD 戰(zhàn)術(shù)都學(xué)習(xí)完了,應(yīng)付日常的代碼開(kāi)發(fā)也夠了,不過(guò)作為架構(gòu)師來(lái)說(shuō),探索的道路還不能止步于此,接下來(lái)會(huì) DDD 戰(zhàn)略部分。戰(zhàn)略部分關(guān)注點(diǎn)有 3 個(gè):

  • 統(tǒng)一語(yǔ)言
  • 領(lǐng)域
  • 限界上下文

1. 統(tǒng)一語(yǔ)言

統(tǒng)一語(yǔ)言的重要性可以根據(jù) Jeff Patton 在《用戶故事地圖》中給出的一副漫畫(huà)來(lái)直觀的描述:

image

統(tǒng)一語(yǔ)言是提煉領(lǐng)域知識(shí)的輸出結(jié)果,也是進(jìn)行后續(xù)需求迭代及重構(gòu)的基礎(chǔ),統(tǒng)一語(yǔ)言的建立有以下幾個(gè)要點(diǎn):

(1) 統(tǒng)一語(yǔ)言必須以文檔的形式提供出來(lái),并且在整個(gè)項(xiàng)目組的各團(tuán)隊(duì)達(dá)成共識(shí);

(2) 統(tǒng)一語(yǔ)言必須每個(gè)中文名有對(duì)應(yīng)的英文名,并且在整個(gè)技術(shù)棧保持一致;

(3) 統(tǒng)一語(yǔ)言必須是完整的,包含以下要素:

  • 領(lǐng)域模型的概念與邏輯;
  • 界限上下文(Bounded Context);
  • 系統(tǒng)隱喻;
  • 職責(zé)的分層;
  • 模式(patterns)與慣用法。

2. 領(lǐng)域劃分

以事件風(fēng)暴的形式(Event Storming),列出所有的用戶故事(Use Story),用戶故事可通過(guò) 6W 模型來(lái)構(gòu)建,即描寫(xiě)場(chǎng)景的 Who、What、Why、Where、When 與 hoW 六個(gè)要素。然后圈選功能相近的部分,就形成了領(lǐng)域,領(lǐng)域又根據(jù)職能不同劃分為:核心域、支撐域、通用域,

具體的過(guò)程有很多參考資料,這里不在細(xì)講,最終的輸出是領(lǐng)域劃分圖,以下是一個(gè)保險(xiǎn)業(yè)務(wù)示例:

image

3. 限界上下文

限界上下文包含兩部分:上下文(Context)是業(yè)務(wù)目標(biāo),限界(Bounded)則是保護(hù)和隔離上下文的邊界。

比如上圖中的實(shí)現(xiàn)部分即是限界上下文的邊界,虛線部分代表了領(lǐng)域的邊界。限界上下文沒(méi)有統(tǒng)一的劃分標(biāo)準(zhǔn),需要的讀者根據(jù)自己的業(yè)務(wù)場(chǎng)景來(lái)甄別如何劃分。

一個(gè)上下文中包含了相同的領(lǐng)域知識(shí),角色在上下文中完成動(dòng)作目標(biāo);

邊界體現(xiàn)在以下幾方面:

  • 領(lǐng)域邏輯層:確定了領(lǐng)域模型的業(yè)務(wù)邊界,維護(hù)了模型的完整性與一致性,從而降低系統(tǒng)的業(yè)務(wù)復(fù)雜度;
  • 團(tuán)隊(duì)合作層:限界上下文一般也是用戶換分團(tuán)隊(duì)的依據(jù);
  • 技術(shù)實(shí)現(xiàn)層:限界上下文可當(dāng)成是微服務(wù)的劃分邊界;

DDD 的不足

DDD 架構(gòu)作為一套先進(jìn)的方法論,在很多場(chǎng)景能發(fā)揮很大價(jià)值,但是 DDD 也不是銀彈。高級(jí)的架構(gòu)師把 DDD 架構(gòu)當(dāng)成一種工具,結(jié)合其他架構(gòu)經(jīng)驗(yàn)一起為業(yè)務(wù)服務(wù)。

DDD 的不足有幾個(gè)方面:

  1. 性能:DDD 是基于聚合來(lái)組織代碼,對(duì)于高性能場(chǎng)景下,加載聚合中大量的無(wú)用字段會(huì)嚴(yán)重影響性能,比如報(bào)表場(chǎng)景中,直接寫(xiě) SQL 會(huì)更簡(jiǎn)答直接;
  2. 事務(wù):DDD 中的事務(wù)被限定在限界上下文中,跨多個(gè)限界上下文的場(chǎng)景需要開(kāi)發(fā)者額外考慮分布式事務(wù)問(wèn)題;
  3. 難度系數(shù)高,推廣成本大:DDD 項(xiàng)目需要領(lǐng)域?qū)<覍<?,且需要特別熟悉業(yè)務(wù)、建模、OOP,對(duì)于管理者來(lái)說(shuō)評(píng)估一個(gè)人是否真的能勝任也是一件困難的事情;

總結(jié)

本文從 MVC 架構(gòu)開(kāi)始講述了如何從演進(jìn)到 DDD 架構(gòu),限于篇幅很多 DDD 的知識(shí)點(diǎn)沒(méi)有講到,希望大家在實(shí)踐過(guò)程中能靈活運(yùn)用,盡享 DDD 給業(yè)務(wù)帶來(lái)的價(jià)值。

責(zé)任編輯:趙寧寧 來(lái)源: 開(kāi)源博客
相關(guān)推薦

2023-07-04 07:53:53

MVCDDD架構(gòu)

2024-04-08 07:05:10

MVCDDD架構(gòu)

2022-06-08 16:55:56

服務(wù)器Redis架構(gòu)

2017-10-30 09:09:41

2015-04-07 11:05:15

VMwareOpenStack

2009-04-30 15:56:50

三層架構(gòu)MVCMVP

2024-08-05 01:29:47

MVC架構(gòu)模式分離模型

2022-07-01 08:26:22

區(qū)塊鏈去中心化以太坊

2023-08-28 16:10:00

容器化DockerKubernetes

2024-11-29 08:40:34

2022-12-08 09:31:07

DDD模型驅(qū)動(dòng)

2024-06-14 08:19:45

2014-11-06 13:35:03

負(fù)載均衡應(yīng)用交付

2024-04-22 08:10:29

2023-10-11 07:29:21

2024-06-07 07:41:03

2023-12-22 08:00:00

2022-01-12 07:06:42

DPU網(wǎng)卡GPU

2018-06-19 09:54:22

MySQLHBase存儲(chǔ)

2020-04-03 13:12:09

函數(shù)架構(gòu) Serverless
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)