DDD領域驅動設計:業務分析神奇


1. DDD設計篇:運用事件風暴法進行業務領域建模、統一語言建模

1.1 如何成為優秀架構師?

架構師 = 技術大牛?

架構師不僅需要懂技術,還要懂業務。

只有將業務落地到技術,開發出對用戶有價值的產品,技術才是有價值的。

什么是業務架構師?

掌握了業務領域知識,掌握了業務痛點,然后用技術方案,解決業務痛點,才能為公司創造價值。

架構師

  • 要能夠將業務轉換為技術

  • 能合理運用技術支撐業務

客戶買單,不是買技術,而是買你用技術解決我業務痛點的技術方案。

所以上面一步步分析后,業務痛點是關鍵

1.2.1 對業務及其痛點,有深刻的理解與思考

不僅要理解業務,還要挖掘出業務痛點,才才可以用對應的技術來解決問題

  • 分析業務流程

  • 理解業務規則

  • 挖掘業務痛點

1.2.2 能夠將技術落地,產生業務價值

  • 規划高遠卻落不了地
  • 快速研發產生業務價值

關鍵難題:

  • 如何快速有效地學習業務領域知識
  • 如何深入地理解與挖掘業務痛點
  • 如何通過技術的手段落地業務

image-20211006193444872

系統沒那么復雜時,需要領域驅動嗎?

系統不復雜時,領域驅動有點大材小用,以前互聯網時代沒到來,沒有發生數據和需求井噴,業務也不復雜,所以不會流行起來。

現在基本都需要,特別是互聯網業務。

1.2 領域驅動設計在什么時候起作用?

1.2.1 在新項目開發中起作用?

image-20211006193826980

  1. 深刻理解業務,進行領域建模

  2. 然后把業務轉換為領域模型,

  3. 再用領域模型指導數據庫設計和程序設計

  4. 按照貧血模型或者充血模型轉換為程序設計

這個時候,一開始需要花大量時間進行領域建模。

但是新項目有一些特點

1、復雜度沒那么高

2、要快速交付

怎么辦?一開始沒辦法,用領域驅動,是考慮長遠,在日后,項目需求變更、維護。

1.2.2 在老項目維護中起作用?

image-20211006194413194

image-20211006194424835

面對老項目越來越復雜時,領域驅動會更好的發揮作用

image-20211006194714773

快速交付?

通過領域驅動設計,提高維護的質量,使得可以在業務不斷迭代、市場激烈競爭、技術快速更新、系統越來越龐大時,依然能快速交付高質量的產品。

我們采用整潔架構,將業務代碼和技術框架,通過一個中間層進行解耦。

今后,我們的業務代碼隨着業務不斷迭代,技術框架也可以不斷迭代調整,互不影響。

1.2.3 在技術架構演化中起作用?

1.3 領域驅動的解決之道

image-20211006195220682

首先,業務負責人(或產品經理),以故事的形式,將功能分發給多個敏捷團隊,每個敏捷團隊負責一個功能模塊。

每個敏捷團隊,在開發的過程中,結合領域驅動設計和微服務架構理念,設計功能。

我們按照業務去拆

微服務如何拆分?

接口怎么划定?

怎么將每次的需求只落到一個微服務里?

讓每個微服務,都是軟件變化的一個原因,

今后因為這個原因而發生的變更,都只發生在這個微服務。微服務的優勢才真正發揮出來了。

每次需求變更時,基於領域驅動設計,進行業務建模,

最后把對業務建模的變更,落實到對微服務的變更

我們的系統才能高質量的低成本維護下去。

在我們結合領域驅動設計和微服務架構理念去做設計時,都是有一定的成本的,從而提高了系統設計的復雜度。

復雜度的增加,進一步降低了交付速度。又和我們的初衷產生矛盾。

我們的初衷,通過領域驅動設計和微服務,簡化設計,降低維護成本,提高交付速度

而使用它們的成本,又會降低交付速度

這個矛盾怎么解決?

架構團隊,架構一個支持微服務,支持領域驅動的架構,把我們在領域驅動中的一些復雜的設計,比如聚合、倉庫、工廠下沉,落地到技術框架里,

一邊基於業務領域模型,建立業務領域層,

一邊將各個技術框架,通過適配器進行解耦。

這就是整潔架構。

image-20211006201106413

1.3.1 為什么需要領域驅動設計指導微服務的划分?

首先看下,傳統的煙囪式的數據庫設計

image-20211006195535129

這樣的數據庫設計,使用的時候,比如獲取商品信息這個數據的時候,各個微服務都要去讀取商品表,

一旦商品表發生變更,各個模塊都要變更代碼。

這樣維護成本非常高。

怎么辦?

那我們希望某一次變更,只需要改某個微服務的代碼,縮小需求變更的影響范圍

1.3.2小而專的微服務設計

小:拆分微服務

專:單一職責原則(容易被忽略)

怎么做?

要求每個表只能有一個微服務直接操作數據庫,其他微服務想要操作是不行的,只能調用對應微服務的接口。

這樣設計后,商品表變更,只會影響到商品維護這個微服務。

只要對外接口不變,其他微服務也不需要改代碼,影響很小

image-20211006200056410

對數據表進行規划,哪個微服務操作哪些表。

提高內聚,從業務角度分析整理,划分清楚邊界。

業界現在最有效的做這個事情的方法,就是領域驅動設計,

通過對業務的梳理,逐步建立起限界上下文。

然后基於限界上下文設計和開發系統

1.3.3 跨庫關聯查詢解決方案

image-20211006202338414

方案:按照領域模型建模,建立好關聯關系,底層架構自動幫我補填數據。

比如我下單,我只關心查訂單數據,我建立領域模型時,建立了訂單和其他模塊的業務關系,配置好業務關系,底層架構自動幫我補填數據。

1.4 本課程要解決的問題

領域驅動設計的作用與意義

真正的目的不在於開發新項目,新項目沒有那么復雜,而在於,日后的維護,系統越來越復雜時,體現出來。

一開始用領域驅動的意義,在於,日后的維護,系統復雜的時候,提高代碼質量,降低交付時間。

怎樣正確地進行業務領域建模

今天的內容

事件風暴

image-20211006203725526

image-20211006203738605

image-20211006203906407

image-20211006203928922

image-20211006203953635

需求變更

image-20211006204039799

image-20211006204124648

為什么越來越復雜?

這是軟件發展的必然規律

image-20211006204204655

image-20211006204232919

傳統的設計方式,隨着新需求的到來,不斷地往payoff()方法里塞代碼。

這樣會導致代碼質量不斷下降,維護成本不斷提高。

軟件退化的根源,不是需求變更

當我們需求變更的時候,我們適時地調整代碼。進行解耦和功能擴展,再去實現需求,就能夠實現高質量代碼

image-20211006204552356

那么我們怎樣適時調整代碼,解耦和擴展呢?

當發生10次、50次、100次變更后,可能就迷失方向了

我們需要一個方法,無論多少次變更,都能夠產生高質量代碼

當我們需求變更時,把變更還原到真實世界里,真實世界是什么樣子,我們軟件就怎么變更。

這樣,無論發生多少次變更,我們也能找到方向。

image-20211006204846712

在領域模型里變更,思考設計

image-20211006205024005

面對變更,傳統設計方式如下圖,直接塞代碼。

image-20211006205055562

而領域驅動的設計方式,我們先將新需求,還原到領域模型上,進行分析

image-20211006205239226

image-20211006205252884

我們要增加折扣,而且有多種不同方式的折扣。

我們需要思考,付款和折扣有什么關系?

折扣的功能,怎么添加到現有功能里?

傳統思考方式:折扣是在付款的時候折扣,就寫在付款的邏輯里

那么難道不寫在付款里?為什么不該寫在付款里?是基於什么原則去思考的嗎?

有,單一職責原則

image-20211006210027762

image-20211006210129424

image-20211006210145290

image-20211006210218981

抽象出限界上下文,每個限界上下文就是一個微服務

每個微服務是軟件變化的一個原因,有需求變更,只需要改某個微服務

怎樣將模型轉換為程序設計

明天分享

支持領域驅動設計的架構設計

第三天的分享內容

1.5 單一職責原則(SRP)

軟件系統中的每個元素只完成自己職責內的事,將其他的事交給別人去做。

“職責”通常理解為一個事情,與該事情相關的事都是它的職責。

一個所謂職責,其實是軟件變化的一個原因。

軟件質量如何衡量?高還是低?

關鍵:維護成本

一個需求來了,我要改3個模塊的代碼

一個需求來了,我要改1個模塊的代碼

要求我們

平時,就要整理代碼。

把同一個原因的代碼放一起。

不同原因的代碼分開放

image-20211006205845163

現在要增加折扣,問自己兩個問題:

付款變了,要不要變折扣?不要

折扣發生變更,付款要不要變?要

折扣是付款的另一個變化原因,不應該把折扣放進付款里

1.6 領域驅動設計與傳統軟件開發有什么不同?

項目不變更,不需要添加新功能,不會越來越復雜,就不需要使用DDD

新項目里用領域驅動設計,是為了日后需求變更,維護起來更容易。

按照領域驅動設計,每次變更的時候,不是直接coding,而是把新需求,先放到領域模型里,在領域模型里設計,還原到真實世界。

領域驅動設計更適合老項目

是不是增加了工作量呢?

需要有一個支持領域驅動的底層架構

如何設計這樣一個底層架構?

在第三天的課里

1.7 案例:遠程智慧醫療系統

對前面的思想進行實戰

image-20211006210951136

1.7.1 期初:傳統的診所管理系統

image-20211006211029507

護士接待患者,指引患者掛號,

然后由醫生去看

醫生建議患者去檢查,做B超、驗血

患者去交錢,驗血

醫生看報告,開葯

患者交錢去葯房拿葯

1.7.2 統一語言與領域建模

image-20211006212048308

用客戶的語言,理解業務

1.7.3 事件風暴

image-20211006212510847

image-20211006212558102

迭代會議之前,開一個事件風暴會議

image-20211006212811086

image-20211006212938651

已確診和已開葯是否是同一個事件?

image-20211006213010632

查找醫生算不算一個事件?

只是個查詢,不需要記錄,預約了才要記錄

領域事件分析全了么?還有已排班,已注冊

image-20211006213213808

分析和事件相關聯人和事

image-20211006213349061

預約是一個命令

觸發者 是患者

相關的還有預約時間

出診計划,哪個醫生、哪個科室,什么時間出診

梳理人和事的關系,

人和事是不是聚合

image-20211006213556589

image-20211006213612230

葯品明細和處方是一個部分和整體的關系,有聚合關系,貼一個紫色便簽

是不是聚合關系?

如果是聚合關系,部分的生命周期一定是在整體里

比如,處方產生了,才有葯品明細,當處方刪除,葯品明細才會刪除

1.7.4 領域建模

image-20211006214033246

image-20211006214313502

image-20211006214215846

image-20211006214413014

2 DDD實踐篇:通過領域模型落地系統設計:數據庫、聚合、工廠與倉庫

image-20211006220303418

2.1 將領域模型轉為數據庫設計

表、表之間的關系

2.2 將領域模型轉為程序設計

image-20211006220527857

實體、值對象(不要太糾結)

實體:每個同學,有一個編號學號,

值對象:同學、老師

image-20211006220756643

貧血模型的設計

image-20211006221030021

充血模型的設計

image-20211006221117782

充血模型的service,把訂單作為一個整體,訂單對象里有依賴的其他對象的,定義了他們的關系。訂單service不需要處理訂單和訂單明細的依賴關系。

訂單service只需要保存訂單,也不需要關心保存到兩個表

image-20211006221417059

訂單和訂單明細是聚合關系,訂單倉庫來做保存到具體的表的事情。

查詢的時候,訂單工廠,會補查用戶、用戶地址等信息

好處:

service不需要關注底層,只需要關注業務邏輯判斷

是不是要為每個領域對象設計一個倉庫和工廠?

框架

2.3 聚合

image-20211006222050553

對象和對象之間有整體和不分關系的時候,只操作整體

訂單和訂單明細是整體與部分的關系。聚合關系

查詢和保存操作都是對訂單,訂單明細的查詢和保存操作在訂單里完成

訂單就是聚合根

2.4 工廠(Factory)/倉庫(Repository)

image-20211006222527714

2.5 領域驅動設計的分層架構

image-20211006222620189

dao變成了倉庫,做了更具體的事情

2.6 互聯網轉型:遠程接診平台

image-20211006222804928

2.7 互聯網轉型帶來的陣痛

image-20211006222846608

2.8 系統規划與架構設計

image-20211006223009698

互聯網轉型后的領域建模設計

image-20211006223053780

image-20211006223316190

image-20211006223443525

image-20211006223636714

image-20211006223831853

image-20211006223847585

image-20211006224114736

image-20211006224957236

CQRS命令查詢指責分離

image-20211006225216710

image-20211006225420095

image-20211006225500612

image-20211006225808784

2.9 四色建模法

image-20211006225922971

image-20211006230043182

image-20211006230218464

image-20211006230338555

image-20211006230442460

image-20211006232007375

image-20211006232037931

通過適配器,解耦業務代碼和技術架構

image-20211006234343393

image-20211006234704540

image-20211006234814257

image-20211006234839997

image-20211006234925225

image-20211006235207792

image-20211006235254381

image-20211006235423299

image-20211006235458621

image-20211006235701389

image-20211006235740810

image-20211007000014674

image-20211007000038638

image-20211007000054822

image-20211007000203707

image-20211007000237235

image-20211007000247415

image-20211007003812957

image-20211007005122362

image-20211007005237941

image-20211007005416460

image-20211007005508960

image-20211007005718782

image-20211007010113105

image-20211007010249353

總結

  • 領域驅動設計適合大型復雜項目,不適合初始項目

  • 領域驅動設計適合頻繁需求變更的項目

  • 通過領域建模,划分限界上下文,根據限界上下文拆分微服務

  • 有新需求或者需求變更,先將需求放在領域模型內討論設計,再將領域模型轉為數據庫設計和程序設計

  • 領域驅動設計時,可以采取事件風暴會議形式,和領域專家一起,使用統一語言討論,一起設計領域對象和關系

  • 事件風暴會議需要確定一些事情:分析有哪些領域事件、確定有哪些和領域事件相關的人和事、確定觸發事件的命令、確定領域事件關聯的人和事有沒有聚合關系

  • 將領域模型分配到各個限界上下文中,構建上下文地圖

  • 新需求的代碼寫在哪個模塊里,采用單一職責原則作為指導

  • 單一職責中的職責是指引起軟件變化的一個原因

  • 平時,就要整理代碼,把同一個原因的代碼放一起,不同原因的代碼分開放

  • 軟件退化的根源不是需求變更,而是在需求變更時,沒有及時調整代碼,沒有正確的調整代碼

  • 貧血模型設計的代碼,領域對象只有狀態,service層包含各種行為,service層很重

  • 充血模型設計的代碼,更符合面向對象,領域對象不僅封裝了狀態,還有各種修改狀態的行為,service層更輕,service層只處理業務邏輯的封裝、事務、權限


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM