成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

DDD戰術篇:領域模型的應用

開發 開發工具
我們會對戰略建模過程中識別出來的問題子域進行抽象,而通過抽象來指導最后的落地實現。本篇文章談談這個抽象過程中大家經常遇到的一些困惑。

領域驅動設計DDD在戰術建模(后文簡稱建模,除非特別說明)上提供了一個元模型體系(如下圖),通過這個元模型我們會對戰略建模過程中識別出來的問題子域進行抽象,而通過抽象來指導***的落地實現。

DDD構建的元模型元素腦圖

(DDD構建的元模型元素腦圖)

這里我們談的戰術階段實際就是這樣一個抽象過程。這個抽象過程由于元模型的存在實際是一定程度模式化的。這樣的好處是并非只能技術人員參與建模,業務人員經過一定的培訓也是完全可以理解的。在帶領不少團隊實踐建模的過程中,業務人員參與戰術設計也是我要求的。

由于已經有不少書籍介紹DDD的元模型,這里我們就不再贅述,轉而談談這個抽象過程中大家經常遇到的一些困惑。這些比較常見的問題可能是DDD元模型未來演進需要解決的,但我們仍然要注意業務問題和架構設計的多樣性,不要過度規范,以至于過猶不及。

業務對象的抽象

通過對業務問題的子域劃分,我們找到了一些關鍵的業務對象。在開始進行抽象前一個必須的步驟就是“講故事”!

講什么故事呢?關于這個子域解決的業務問題或者提供的業務能力的故事。既然是故事,就必須有清晰的業務場景和業務對象之間的交互。這件事情看起來是如此自然和簡單,然則一個團隊里能夠站起來有條不紊陳述清楚的卻沒有幾人。讀到這里的讀者不妨停下來試試,你是否能夠把現在你所做的業務在兩三分鐘內場景化地描述出來?

[[210247]]

這么做顯然目的是讓我們能夠比較完整地思考我們所要提煉和抽象的業務對象有哪些。只有當我們能夠“講”清楚業務場景的時候,才應該開始抽象的步驟。對于一個業務對象,我們常見的抽象可以是“實體”(Entity)和“值對象”(Value Object)。

這兩個抽象方式在定義上的區別是,實體需要給予一個唯一標識,而值對象不需要(可以通過屬性集合標識)。當然另外一個經常引用的區別是,實體應該是有一個連續的生命周期的,比如我們在一個訂單跟蹤領域里抽象訂單為一個實體,那么每個訂單應該有一個唯一識別號,訂單也應該有從下單創建到***交貨完成的生命周期。

顯然,如果不增加其它約束條件,值對象的抽象是沒有意義的,都用實體不就行了?但如果我們稍微思考一下一個實體的管理成本,比如需要保證生命周期中實體狀態的一致性,那么我們就會發現值對象變得很簡單很可愛。當一個對象在我們(抽象)的世界里不能改變的時候,一切都變得簡單了,這個對象被創建后只能被引用,當沒有引用時我們可以把它交給垃圾回收自動處理。

隨著高并發、分布式系統的普及,實際上我們在對業務對象抽象的***步思考是能否用值對象。如果大家實現的技術架構采用函數范式的語言(類似Closure),那么首先考慮值對象抽象可能就是一個建模原則了。

對象抽象初步完成后,一定要再重復一次之前的故事來審視一下我們的建模。經歷這個抽象過程后,參與討論的每個人都應該發現自己更清晰業務的需求和需要提供的能力了。

聚合的封裝

DDD元模型中一個核心概念叫“聚合”(Aggregate)。這個從建筑學來的名詞非常形象,建筑學上我們翻譯為“骨料”,是形成混凝土的重要元素,也是為什么混凝土如此堅固的基礎。

[[210248]]

(混凝土里的一種骨料)

同理,在DDD建模中,聚合也是我們構建領域模型的基礎,并且每個聚合都是內聚性很高的組合。聚合本身完成了我們對骨干業務規則的封裝,減小了我們實現過程中出錯的可能。

以上面那個訂單跟蹤領域為例,假設我們允許一個訂單下存在多個子訂單,而每個子訂單也是可以獨立配送的,這種情況下我們抽象出“子訂單”這個實體。顯然訂單和子訂單存在業務邏輯上的一致性,沒有訂單的時候不應該創建子訂單,更新子訂單的時候應該同時“通知”所屬的訂單。這個時候如果采用把訂單和子訂單聚合起來的封裝就很有必要了。

采用聚合抽象的結果就是訪問每個子訂單都需要從相關的訂單入口(i.e., 訂單為聚合根),存取時我們都是以這個聚合為基本單位,即包含了訂單和訂單下面的所有子訂單。顯然這樣的好處是在訂單跟蹤這個領域模型里,訂單作為一個聚合存在,我們只需要一次性梳理清楚訂單和子訂單的邏輯關系,就不需要在未來每次引用時都考慮這里面的業務規則了。

訂單跟蹤領域的訂單聚合

(訂單跟蹤領域的訂單聚合)

在建模過程中,很多團隊并沒有努力思考聚合的存在。封裝這個在技術實現領域的基本原則在建模時卻很少被重視起來。開篇提到在戰術建模過程中強調業務領域人員的參與也是為了解決這個問題,聚合的識別實際是針對業務規則的封裝,當我們不理解業務規則的時候是無法做出是否封裝的判斷的。

一言以蔽之,識別聚合是認知潛在核心業務規則的過程,而定義出來的聚合是在大家共識基礎上對核心業務規則的封裝。

領域服務的定義

在最初的元模型定義里,領域服務讓不少人糾結,一個經典的例子是在賬戶管理領域里對“轉賬”這個業務行為的抽象。由于轉賬本身是作用在至少兩個賬戶上的,所以把轉賬作為一個賬戶的行為顯然是不合適的。那么如果我們把轉賬名詞化抽象成一個實體呢?感覺也是比較別扭,畢竟轉賬是依附于賬戶存在的。

這個時候DDD在元模型里提出了服務(Service)這個抽象,轉賬被抽象為一個服務感覺就順暢多了。同樣道理,在我們上面的訂單跟蹤領域里,如果跟蹤的過程中需要進行短信的通知,一個比較好的建模就是抽象出一個“通知”服務來完成。

我經常會用靜態方法來幫助技術人員理解服務的抽象(雖然服務并不一定用靜態方法來實現)。服務本身就像一個靜態方法一樣,擁有一定的邏輯但不持有任何的信息,從整個領域來看也不存在不同“版本”的同一個服務。

[[210249]]

一個經常困擾大家的問題是對Service這個詞語的限定,有的分層架構設計里會出現領域服務(Domain Service)和應用服務(Applicaiton Service)。大多數時候應用服務在領域服務的上層,直接對外部提供接口。如果存在這樣的分層,那么領域服務就不應該直接對外,而應該通過應用服務。

舉個例子,前面的訂單消息通知如果是一個領域服務,在完成訂單狀態變化時創建通知消息,而***的通知以短信的方式發給設定的人群,這樣就應該有一個相應的應用服務,包含了具體的業務場景處理邏輯。之后也可能有一個郵件通知的應用服務,同樣調用了這個通知領域服務,但通過郵件渠道來完成最終的業務場景。

由于微服務架構的流行,每個子領域的粒度已經相當細了,很多時候已經沒有這樣的領域服務和應用服務的區分了。當然從簡單性角度出發這是好事情。在整個建模過程中,服務的抽象往往是最不確定的,也是最值得大家反復斟酌的地方。

Repositories的使用

Repositories是一個非常容易被誤解的抽象,很多人會直接聯想到具體的數據存儲。在初期采用DDD建模的時候,我經常刻意回避這個抽象,避免讓大家陷入思考紊亂。

[[210250]]

這個抽象概念實際可以追溯到Martin Fowler的Object Query模式。另外一個相關概念是DAO(Data Access Object),都是用來簡化需要存儲的數據和對應的業務對象之間的映射關系。不同的是Repositories針對更加粗顆粒度的抽象,在DDD這個方法里我們可以認為映射對象是我們的聚合。針對每個實體在實現時候也可能創造出對應的DAO(比如采用Hibernate這樣的ORM框架),但顯然在建模過程中不是我們需要關注的。

那么Repositories的抽象為什么是必要的呢?讓我們再回到訂單跟蹤這個例子,通知訂單狀態發生變化的服務在發出通知前,需要定位到訂單的信息(可能包括訂單的相關干系人和子訂單的信息)。通知作為一個服務是不應該持有具體訂單信息的,這個時候我們就需要通過Repositories的抽象來建立對訂單這個聚合的查詢,即有一個訂單的repo,而具體的查詢邏輯應該在這個repo中。

這樣的抽象在需要存儲和查詢值對象的時候也是必要的。假設我們分析訂單查詢這個領域,在這個領域里訂單記錄顯然已經不允許修改了,自然的抽象方式就是值對象。同時一個查詢的服務來持有具體的查詢邏輯(比如按時間或用戶)是合理的。外部應用直接調取了查詢服務(接口)并給出規定的參數,我們就需要一個訂單記錄的repo來持有跟存儲相關的查詢邏輯。當然這并不是說有一個查詢就一定有一個repo與之對應,如果查詢的邏輯非常簡單,未嘗不可以讓服務直接針對數據存儲實現。記住我們抽象的目標是讓建模更簡單,抽象過程中應該保持靈活。

限界上下文的意義

經過最近10多年的演進,我們在如何支撐一個組織的規?;线_成了一些基本的共識。我們知道微服務架構(Microservices)能夠幫助我們把成百上千的工程師們組織起來,而小團隊的自組織性是至關重要的。我們也逐步就如何能夠在技術和業務團隊之間明確溝通“架構”這個難題上找到了DDD。那么DDD和微服務架構的關系是什么呢?很多人會提到限界上下文(Bounded Context)。

我曾經就這個話題專門撰文一篇(DDD&Microservices)。一個限界上下文封裝了一個相對獨立子領域的領域模型和服務。限界上下文地圖描述了各個子領域之間的集成調用關系。這個定義某種意義上和我們的微服務劃分不謀而合:以提供業務能力為導向的、自治的、獨立部署單元。所以雖然我們不能***依據限界上下文劃分服務,但限界上下文,或者說是DDD,絕對是我們設計微服務架構的重要方法之一。

如果我們再追溯到DDD的戰略設計,我們會發現在問題域上,DDD通過子問題域(subdomain)的劃分就已經進行了針對業務能力的分解,而限界上下文在解決方案域中完成了進一步分解。當然我們不能完全認為子問題域和限界上下文有嚴格意義上的一對一關系,但大多數情況下一個子問題域是會被設計成一個或多個限界上下文的。子域subdomain和限界上下文某種意義上是互相印證的,重點在區分問題域和解決方案域,這是落地DDD最困難的地方,也是判斷一個架構師能力進階的分水嶺。

戰術建模小結

DDD的建模元素比較簡潔,本文中敘述的元模型應該是滿足了大多數場景下的建模。毛主席曾經有一句名言“戰略上要藐視敵人 戰術上要重視敵人”,就架構設計來說我們沒有敵人,業務需求是我們的朋友。所以在領域驅動的架構設計方面,咱們需要的是“戰略上要重視朋友,戰術上要簡化建模”。希望這句話能夠幫助正在實踐DDD的團隊重新思考自己在戰略問題域的投入和重視程度,不要揮舞著戰術模型的大錘到處尋找實際不存在的釘子。

【本文是51CTO專欄作者“ThoughtWorks”的原創稿件,微信公眾號:思特沃克,轉載請聯系原作者】

戳這里,看該作者更多好文

責任編輯:趙寧寧 來源: 51CTO專欄
相關推薦

2023-02-20 14:44:22

DDD領域模型

2024-11-27 15:33:17

軟件架構DDD

2022-04-19 08:15:53

DDD領域建模實戰

2021-09-08 09:22:23

領域驅動設計

2023-02-15 13:50:58

DDD戰略設計

2023-02-19 12:44:07

領域事件DDD

2023-02-26 10:59:51

2025-03-28 09:46:05

AI算法AI人工智能

2024-07-09 11:01:24

2024-04-02 07:25:19

大語言模型青少年編程NLG

2023-08-29 08:57:03

事務腳本架構模式業務場景

2017-07-14 10:55:05

2021-06-30 07:51:09

新項目領域建模

2022-04-07 07:51:40

代碼結構設計

2021-10-09 11:54:46

DDD微服務業務

2025-01-26 10:10:30

2020-03-18 13:28:29

SpringDDDWeb

2024-06-18 08:21:31

2024-05-10 06:59:06

2010-10-07 20:57:37

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 亚洲网站在线播放 | 98久久| 久久国产亚洲 | 欧美天堂| 亚洲一区二区三区在线视频 | 羞羞网站在线免费观看 | 成人精品鲁一区一区二区 | 青青草华人在线视频 | 91久久北条麻妃一区二区三区 | 四虎在线播放 | 日韩电影一区 | 色黄爽| 亚洲电影在线播放 | 欧美黄色性生活视频 | 精品日韩电影 | 99爱国产 | 亚洲精品www | 亚洲小视频 | 美日韩一区二区 | 91精品国产91久久久久游泳池 | 国产精品96久久久久久 | 天天拍天天操 | 国产精品视频在线播放 | 人人射人人草 | 国产精品1区 | 看片91 | 午夜精品一区二区三区在线视频 | 久久久久久久久久毛片 | 亚洲欧美日韩一区 | 国产视频久久久 | 久久av.com| 四季久久免费一区二区三区四区 | 国产一区二区久久 | 一区二区三区四区免费观看 | 亚洲国产成人av好男人在线观看 | 一区免费观看 | 精品国产乱码久久久久久蜜柚 | 国产精品美女久久久久久久网站 | 东方伊人免费在线观看 | 久操伊人 | 成人在线观看欧美 |