欧美人与禽2O2O性论交,秋霞免费视频,国产美女视频免费观看网址,国产成人亚洲综合网色欲网

對領域驅動設計的理解與實踐(對領域驅動設計的理解與實踐怎么寫)

領域驅動設計(domain-Driven-Design)是一種針對大型復雜系統(tǒng)的領域建模與分析方法論。

2003 年,Eric Evans 發(fā)布《Domain-Driven Design: Tackling Complexity in the Heart of Software》(領域驅動設計:軟件核心復雜性應對之道),其中定義了DDD。

DDD改變了傳統(tǒng)軟件開發(fā)針對數據庫進行的建模方法;DDD先對業(yè)務領域進行分析,建立領域模型,根據領域模型驅動代碼設計。合理運用面向對象的高內聚低耦合設計要素,降低整個系統(tǒng)的業(yè)務復雜性,并使得系統(tǒng)具有更好的擴展性,更好的應對多變的業(yè)務需求。

領域 Domain

一個領域就是一個問題域,只要是同一個領域,那問題域就相同。

只要確定了系統(tǒng)所屬的領域,那么這個系統(tǒng)的核心業(yè)務,即要解決的問題以及問題的邊界就基本確定了。

舉例

陌生人社交領域,包含有聊天,用戶推薦,朋友圈等核心環(huán)節(jié)。 只要是這個領域,一般都會有這些相同的核心業(yè)務,因為他們要解決問題的本質是一樣的,就是交友。

每一個領域,都有一個對應的領域模型,領域模型能夠很好的幫我們解決復雜的業(yè)務問題。

驅動設計

在DDD中,以領域(domain)為邊界,分析領域的核心問題,再設計對應的領域模型,最后通過領域模型驅動代碼設計的實現。這樣設計的系統(tǒng)才有合理的分層與解耦,對以后業(yè)務的迭代開發(fā),代碼的維護才更加容易。

然而很多互聯網公司,為了追求快速的上線,都是模型都沒有想清楚就開始寫代碼,這就導致了后續(xù)代碼維護困難,無法擴展。修改bug同時又引入新的bug,反反復復,進入惡性循環(huán)。

當然,這跟梳理清楚領域模型需要一定時間,這與初創(chuàng)型的互聯網公司需求快速上線有點相悖,但是,這點時間的投入是非常值得的。因為可以避免了系統(tǒng)上線后不久又得重構的問題。

概念總結

  • 領域就是問題域
  • 模型驅動的思想:通過建立領域模型來解決領域中的核心問題
  • 領域建模的目標:針對我們在領域中核心問題,而不是整個領域中的所有問題
  • 領域模型設計:設計時應考慮一定的抽象性、通用性,以及復用價值
  • 代碼實現:通過領域模型驅動代碼的實現,確保代碼讓領域模型落地,代碼最終能解決問題

為什么需要DDD

系統(tǒng)復雜性

耦合

隨著產品不斷的迭代,業(yè)務邏輯變得越來越復雜,系統(tǒng)也越來越龐大。模塊彼此互相關聯、耦合。導致增加或修改一個功能變得異常艱難,同時功能間的界限也變得模糊,職責不再清晰。這個時候就需要進行重構,拆分。

雖然架構本身是隨著業(yè)務進行不斷演進的;但是,如果架構初始設計不體現出業(yè)務的模型,那么新需求就無法體現在現有架構上,導致不斷腐化,不斷重構。

內聚

貧血模型 Anemic Domain Object

domain object僅用作數據載體,而沒有行為和動作的領域對象。

指領域對象里只有get和set方法,沒有相關領域對象的業(yè)務邏輯。業(yè)務邏輯放在業(yè)務層。

充血模型 Rich Domain Object

將業(yè)務邏輯和對象存儲放在domain object里面,業(yè)務層只是簡單進行小部分業(yè)務的封裝及其他domain的編排。

面向對象設計,符合單一職責設計。

貧血 vs 充血

貧血模型的domain object很輕量,這導致業(yè)務層的復雜,domain object相關的業(yè)務邏輯散布在各個業(yè)務層,造成業(yè)務邏輯的冗余以及原本domain object的定義就變得相對模糊,這就是貧血癥引起的失憶癥。

而采用領域開發(fā)的方式,將數據和行為封裝在一起,與業(yè)務對象相映射;領域對象職責清晰,將相關業(yè)務聚合到領域對象內部。

微服務

DDD 的本質是一種軟件設計方法論,而微服務架構是具體的實現方式。微服務架構并沒有定義對復雜系統(tǒng)進行分解的具體方法論,而 DDD 正好就是解決方案。

微服務架構強調從業(yè)務維度來分治系統(tǒng)的復雜度,而DDD也是同樣的著重業(yè)務視角。

DDD能帶來什么

  • 建立通用語言: 圍繞領域模型建立的一種語言,團隊所有成員都使用這種語言進行溝通和活動
  • 驅動代碼設計:領域建立模型,模型指導設計,設計產出代碼
  • 解決核心問題:模型的設計中心就是核心域,就是解決核心的問題

DDD建模

戰(zhàn)略設計

戰(zhàn)略設計就是從宏觀角度對領域進行建模。劃分出業(yè)務的邊界,組織架構,系統(tǒng)架構。

DDD中,對系統(tǒng)的劃分是基于領域的,也是基于業(yè)務的。

通用語言Ubiquitous Language

通用語言是指確定統(tǒng)一的領域術語,提高開發(fā)人員與領域專家之間的溝通效率。

一旦確定了統(tǒng)一語言,無論是與領域專家的討論,還是最終的實現代碼,都可以通過使用相同的術語,清晰準確地定義領域知識。

當確認整個團隊統(tǒng)一的語言后,就可以開始進行領域建模。

領域和子域

領域Domain

一個領域本質上可以理解為就是一個問題域。只要我們確定了系統(tǒng)所屬的領域,那這個系統(tǒng)的核心業(yè)務,即要解決的關鍵問題、問題的范圍邊界就基本確定了。

舉例

  • 社交領域:關鍵問題是用戶推薦,聊天
  • 電商領域:關鍵問題是購物,訂單,物流

子域Subdomain

如果一個領域過于復雜,涉及到的領域概念、業(yè)務規(guī)則、交互流程太多,導致沒辦法直接針對這個大的領域進行領域建模。這時就需要將領域進行拆分,本質上就是把大問題拆分為小問題,把一個大的領域劃分為了多個小的領域(子域),那最關鍵的就是要理清每個子域的邊界

子域可以根據自身重要性和功能屬性劃分為三類子域:

  • 核心域:公司核心產品和業(yè)務的領域
  • 支撐子域:不包含決定產品和公司核心競爭力的功能,也不包含通用功能的子域
  • 通用子域:被多個子域使用的通用功能子域

每個領域的劃分都不一樣。對相同領域公司而言,其核心,支撐,通用的子域也可能有不一樣的地方,但大體上基本都是一樣的。

舉例

社交領域的劃分

  • 核心域:用戶推薦,聊天
  • 支撐子域:客服,反垃圾
  • 通用子域:消息推送

限界上下文Bounded Context

限界上下文

限界指劃分邊界,上下文對應一個聚合,限界上下文可以理解為業(yè)務的邊界。

一個子域對應一個或多個限界上下文。如果對應多個上下文,則可以考慮子域是否要再進行細粒度的拆分。

限界上下文的目的是為了更加明確領域模型的職責和范圍

劃分限界上下文

三個原則:

  • 概念相同,含義不同(通用語言):如果一個模型在一個上下文里面有歧義,那有歧義的地方就是邊界所在,應該把它們拆到不同的限界上下文中。
  • 外部系統(tǒng):有時候系統(tǒng)需要同外部系統(tǒng)交互,這時可以把與外部系統(tǒng)交互的那部分拆分出去以實現更好的擴展性。這樣一旦外部系統(tǒng)發(fā)生了變化,就不會影響到我們的核心業(yè)務邏輯。
  • 組織擴展:盡量不要兩個團隊一起在一個限界上下文里面開發(fā),因為這樣可能會存在溝通不順暢、集成困難等問題。

組織架構

康威定律

任何組織在設計一套系統(tǒng)時,所交付的設計方案在結構上都與該組織的溝通結構保持一致。

團隊結構就是組織結構,限界上下文就是系統(tǒng)的業(yè)務結構。所以,團隊結構應該盡量和限界上下文保持一致。

舉例

社交領域中,訂單子域對應訂單上下文

上下文映射

從宏觀上看每個上下文之間的關系,可以更好理解各個上下文之間的依賴關系。

梳理清楚上下文之間的關系是為了:

  • 任務更好拆分,一個開發(fā)人員可以全身心的投入到相關的一個單獨的上下文中
  • 溝通更加順暢,一個上下文可以明確自己對其他上下文的依賴關系,從而使得團隊內開發(fā)直接更好的對接
  • 每個團隊在它的上下文中能夠更加明確自己領域內的概念,因為上下文是領域的解系統(tǒng)

舉例

聊天上下文依賴消息推送,推廣上下文也依賴消息推送

戰(zhàn)術建模

戰(zhàn)術建模是從微觀角度對上下文進行建模。

梳理清楚聚合根,實體,值對象,領域服務,領域事件,資源庫等。

實體Entity

當一個對象可以由標識進行區(qū)分時,這種對象稱為實體

和數據庫中的實體是不同的,這里的實體是從業(yè)務角度進行劃分的。

實體:

  • 具有唯一標識
  • 持久化
  • 可變

舉例

社交中的用戶即為實體,可以通過用戶唯一的id進行區(qū)分。

值對象value object

當一個對象用于對事物進行描述而沒有唯一標識時,它被稱作值對象。

在實踐中,需要保證值對象創(chuàng)建后就不能被修改,即不允許外部再修改其屬性。

例如:年齡,聊天表情符號( :stuck_out_tongue:: 吐舌 (U 1F61B))

習慣了使用數據庫的數據建模后,很容易將所有對象看作實體

聚合根Aggregate Root

聚合是一組相關對象的集合,作為一個整體被外界訪問,聚合根是這個聚合的根節(jié)點。

聚合由根實體,值對象和實體組成。(聚合根里面有多少個實體,由領域建模決定)

外部對象需要訪問聚合內的實體時,只能通過聚合根進行訪問,而不能直接訪問

舉例

一個訂單是一個聚合根,訂單購買的商品是實體,收貨地址是值對象。

領域服務Domain Service

領域服務

一些既不是實體,也不是值對象的范疇的領域行為或操作,可以放到領域服務中。用來處理業(yè)務邏輯,協調領域對象來完成相關業(yè)務。

例如,有些業(yè)務邏輯不適合放到領域對象中,或實體之間的業(yè)務協調,這些業(yè)務邏輯都可以放到領域服務中。

特征

  • 與領域相關的操作如執(zhí)行一個業(yè)務操作過程,但它又并不適合放入實體與值對象中
  • 操作是無狀態(tài)的
  • 對領域對象進行轉換,或以多個領域對象作為輸入進行計算,結果產生一個值對象

當采用微服務架構風格,一切領域邏輯的對外暴露均需要通過領域服務來進行。

如原本由聚合根暴露的業(yè)務邏輯也需要依托于領域服務。

舉例

必須通過訂單領域服務來創(chuàng)建和訪問訂單

領域事件

領域事件是對領域內發(fā)生的活動進行的建模。捕獲一些有價值的領域活動事件。

作用

  • 解耦:可以通過發(fā)布訂閱模式,發(fā)布領域事件
  • 一致性:通過領域事件來達到最終一致性
  • 事件溯源

舉例

發(fā)送聊天消息,這屬于一個領域事件;撤回消息,也屬于一個領域事件。

推送服務訂閱消息事件,然后將消息推送給用戶端。這樣就解耦了消息服務與推送服務之間的強依賴關系。

資源庫Repository

資源庫用于保存和獲取聚合對象。

領域模型 vs 數據模型

資源庫介于領域模型(業(yè)務模型)和數據模型(數據庫)之間,主要用于聚合對象的持久化和檢索。

資源庫隔離了領域模型和數據模型,以便上層只需要關注于領域模型而不需要考慮如何進行持久化。

分層架構

把一系列相同的對象進行分類放在同一層,然后根據他們之間的依賴關系再確定上下層次關系。

在實際決策時,我們需要知道各層的職責、意義以及相應的場景;

落實到代碼層面時,我們還需要知道各層所包含的具體內容、各層的一些常見的具體策略/模式、層次之間的交互/依賴關系。

DDD經典分層架構

對領域驅動設計的理解與實踐(對領域驅動設計的理解與實踐怎么寫)

  • 用戶接口層(interfaces):處理顯示和用戶請求,以及一些基本的參數檢查,不包括業(yè)務邏輯
  • 應用層(application):主要協調領域對象的操作;處理持久化事務、發(fā)送消息、安全認證等
  • 領域層(domain):處理核心業(yè)務邏輯,不包括技術實現細節(jié)。領域層是業(yè)務軟件的核心
  • 基礎設施層(infrastructure):處理純技術細節(jié),為其他層提供技術支撐,也可用于封裝調用的外部系統(tǒng)細節(jié)。例如:持久化的實現,消息中間件的實現,工具類,rpc

個人理解:這種分層,既可以在一個單體應用中,也可以是微服務的形式。DDD分層并不一定要按微服務的服務粒度進行分層。

如果一個業(yè)務邏輯非常簡單的子域,則可以將幾層都放進一個單體應用中,在應用中進行分層。如果業(yè)務較為復雜,則可以按服務進行拆分,每層都有自己對應的服務。

其他架構

  • 對稱性架構
  • 洋蔥架構
  • 整潔架構
  • CQRS架構

DDD工程實踐

以一個簡化的社交領域的例子來實踐DDD。

核心概念

  • 用戶(User): 一個賬戶,并以用戶id識別
  • 關系(Relationship):用戶之間的關系
  • 動態(tài)(Feed): 用戶發(fā)布文字,圖片,視頻,評論等內容
  • 會話(Conversation):用戶之間的聊天會話

領域設計

戰(zhàn)略建模

領域就是社交領域,核心問題和絕大部分社交系統(tǒng)一樣。

子域

  • 核心域:聊天,動態(tài)
  • 支撐子域:反作弊,推廣
  • 通用子域:用戶,關系,消息推送

上下文

  • 消息上下文
  • 會話上下文
  • 動態(tài)上下文
  • 推送上下文
  • 用戶上下文

戰(zhàn)術建模

以會話上下文為例子來進行戰(zhàn)術建模

會話上下文

  • 會話:聚合根用戶:實體用戶:實體消息列表:實體發(fā)送人:實體接收人:實體消息內容:值對象

消息在會話上下文屬于實體,在消息上下文屬于聚合根。

結構

以會話子域為例

架構分層

  • interfaces 接口層RESTfulRPC
  • application 應用層Conversationmessage
  • domain_service 領域服務層modelConversationMessagerepositoryConversationMessage
  • infrastructure 基礎設施層storeConversationRepositoryMessageRepositorymessageSendMessageutils

領域

package domain// 聚合根type Conversation struct { ID int User1 User User2 User Messages list.List}// 實體type Message struct { ID int From User // 實體 To User Body Content // 值對象}

用戶接口層

type ChatInferface struct { // 應用層 app app.ChatApplication}func (c *ChatInferface) Route() { c.route("POST", "/api/message", c.SendMessage) c.route("PATCH", "/api/message", c.RecallMessage)}// POST /api/messagefunc (c *ChatInferface) SendMessage(ctx *Context) { if !c.validateRequest(ctx) { return } message := c.parseMessage(ctx) app.SendMessage(message)}func (c *ChatInferface) RecallMessage(ctx *Context) { if !c.validateRequest(ctx) { return } messageID := c.parseMessage(ctx) app.RecallMessage(messageID)}

應用層

type ChatApplication struct { user service.UserService chat service.ChatService // 這里領域事件由應用層發(fā)布 // publisher EventPublisher lbs LBSFacade}func (c *ChatApplication) SendMessage(msg *Message) { if !c.user.CheckUser(msg.UserID) { return } c.chat.SendMessage(msg)}

領域服務層

type ChatService struct { // 領域事件 publisher MessageEventPublisher repo MessageRepository}func (c *ChatService SendMessage(msg *Message) { // 業(yè)務邏輯 ... // 領域資源持久化 c.repo.Save(msg) // 發(fā)布領域事件 c.publisher.Publish(msg)}

基礎設施層

package infrastructuretype MessageRepository struct { db MessageDatabase cache MessageCache}func (m *MessageRepository) Save(msg *Message) { db.Save(m.ToPO(msg))}func (m MessageRepository) Get(msgID int) *Message { msg := m.cache.Get(msgID) if msg != nil { return m.FromPO(msg) } return m.FromPO(m.db.Get(msgID))}

總結

在設計和實現一個系統(tǒng)的時候,這個系統(tǒng)所要處理問題的領域專家和開發(fā)人員以一套統(tǒng)一語言進行協作,共同完成該領域模型的構建,在這個過程中,業(yè)務架構和系統(tǒng)架構等問題都得到了解決,之后將領域模型中關于系統(tǒng)架構的主體映射為實現代碼,完成系統(tǒng)的實現落地。

相關新聞

聯系我們
聯系我們
公眾號
公眾號
在線咨詢
分享本頁
返回頂部