日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長提供免費收錄網(wǎng)站服務(wù),提交前請做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

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

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

代碼角度:

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

項目管理角度:

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

第一層:初出茅廬

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

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

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

DDD分層架構(gòu):

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

 

image

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

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

 

image

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

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

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

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

1、領(lǐng)域?qū)?/h1>

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

  • 1個聚合
  • 1到多個實體
  • 若干值對象
  • 多個DomainService
  • 1個Factory:新建聚合
  • 1個Repository:聚合倉儲服務(wù)

聚合根(AggregateRoot)

聚合本身也是一個實體,聚合可以包含其他實體,其他實體不能脫離聚合而單獨提供服務(wù),比如一篇文章下的評論,評論必須從屬于文章,沒有文章也就沒有評論。倉庫層(repository)也必須是以聚合為核心提供服務(wù)的;

實體:可以理解為一張數(shù)據(jù)庫表,必須有主鍵;

值對象:沒有主鍵,依附于實體而存在,比如用戶實體下住址對象,一般在數(shù)據(jù)庫中已json字符串的形式存在;最常見的值對象是枚舉;

倉庫服務(wù)(repository)

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

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

如果是用Mybatis實現(xiàn),那么repository需要加入多個mApper的引用,再手動做拼裝。

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

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

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

 

image

工廠服務(wù)(factory)

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

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

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

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

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

2、應(yīng)用層

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

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

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

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

  • 編排服務(wù):最典型比如Drools;
  • Command、Query命令模式;
  • 業(yè)務(wù)按Rhase、Step逐層拆分模式;
從MVC到DDD的架構(gòu)演進(jìn)

 

image

3、Maven模塊劃分

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

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

 

image

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

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

 

image

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

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

 

image

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

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

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

1、統(tǒng)一語言

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

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

 

image

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

  • 統(tǒng)一語言必須以文檔的形式提供出來,并且在整個項目組的各團(tuán)隊達(dá)成共識;
  • 統(tǒng)一語言必須每個中文名有對應(yīng)的英文名,并且在整個技術(shù)棧保持一致;
  • 統(tǒng)一語言必須是完整的,包含以下要素:
  • 領(lǐng)域模型的概念與邏輯;
  • 界限上下文(Bounded Context);
  • 系統(tǒng)隱喻;
  • 職責(zé)的分層;
  • 模式(patterns)與慣用法。

2、領(lǐng)域劃分

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

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

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

 

image

3、限界上下文

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

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

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

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

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

DDD的不足

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

DDD的不足有幾個方面:

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

總結(jié)

本文從MVC架構(gòu)開始講述了如何從演進(jìn)到DDD架構(gòu),限于篇幅很多DDD的知識點沒有講到,希望大家在實踐過程中能靈活運用,盡享DDD給業(yè)務(wù)帶來的價值。本文如有不足之處敬請反饋。

分享到:
標(biāo)簽:架構(gòu)
用戶無頭像

網(wǎng)友整理

注冊時間:

網(wǎng)站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨大挑戰(zhàn)2018-06-03

數(shù)獨一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運動步數(shù)有氧達(dá)人2018-06-03

記錄運動步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績評定2018-06-03

通用課目體育訓(xùn)練成績評定