DDD(領域驅動設計)學習筆記

〇、引言

DDD是一種非常正確的的設計理念。可以說是對傳統OO的昇華。

相較於現在被大量使用的 web + service + dao 的分層結構,DDD更接近於面向對象設計的本質,或者說其更接近於業務模型,代碼的通用性、複用性、可維護性、擴展性更爲完善。

但是如果完全將其進入工業生產的話,不可避免帶來一個讓人困惑的問題:完全遵循DDD設計規範的話,大多數情況下項目整體效率是高不起來的。DDD究竟能爲企業行爲帶來什麼好處,其推崇的交流、設計方式到底是好是壞?

一、DDD的基礎知識

1-1、什麼是DDD

DDD = Domain Driven Design,即領域驅動設計。

不可能在不瞭解業務知識(最終產品)的前提下進行軟件開發;在開發前,通常需要進行大量的業務知識梳理,而後到達軟件設計的層面,最後纔是開發。

在業務知識梳理的過程中,必然會形成某個領域知識,根據領域知識來一步步驅動軟件設計,就是領域驅動設計的基本概念。

DDD與瀑布模型與敏捷迭代都不同,它的最小單元是領域模型(Domain Model),即能夠精確反映領域中某一知識元素的載體。

業務知識的獲取需要通過與該領域的業務專家進行頻繁的溝通,才能將專業知識轉化爲領域模型。

領域模型無關技術,具有高度的業務抽象性,它能夠精確的描述領域中的知識體系;同時它也是獨立的,我們還需要學會如何讓它具有表達性,讓模型彼此之間建立關係,形成完整的領域架構。

1-2、通用語言

領域專家與軟件設計人員對於一些專業名詞的理解有時候是南轅北轍的。

爲了避免溝通中出現誤解給之後的工作帶來不可彌補的損失,必須約定好雙方都認可的,可以明確表達領域模型的一種通用語言。

這種通用語言沒有標準答案,只要雙方都認可,並且都可以熟練使用即可。

一種方案是UML。UML擅長表述類、屬性和相互之間的關係。但是當領域模型數量急劇膨脹以後,UML就會變得混亂起來。

另一種方案是文檔。但是冗長的文檔需要花費很多的時間和精力,而且有時很難跟得上領域模型自身的修正和迭代。

具體如何取捨完全取決於團隊自身的實際狀況。

但是有一點是肯定的,在建立領域模型的過程中必須將領域建模與軟件設計密切結合起來,模型在構建時就考慮
到軟件和設計。開發人員應當適當參與建模的過程,適時給出反饋。

一個無法被軟件架構設計實現的領域模型無疑是缺乏實用性的。

1-3、分層架構

DDD分層設計

  • 用戶界面/展現層:負責向用戶展現信息以及解釋用戶命令
  • 應用層:很薄的一層,用來協調應用的活動。它不包含業務邏輯。它不保留業務對象的狀態,但它保有應用任務的進度狀態。
  • 領域層:本層包含關於領域的信息。這是業務軟件的核心所在。在這裏保留業務對象的狀態,對業務對象和它們狀態的持久化被委託給了基礎設施層。
  • 基礎設施層:本層作爲其他層的支撐庫存在。它提供了層間的通信,實現對業務對象的持久化,包含對用戶界面層的支撐庫等作用。

1-4、entity

實體與面向對象中的概念類似,是領域模型的基本元素。在領域模型中,實體應該具有唯一的標識符。

1-5、value object

值對象和編程中數值類型的變量不同,它僅僅是沒有唯一標識符的實體,比如有兩個收穫地址的信息完全一樣,那它就是值對象,並不是實體。

值對象在領域模型中是可以被共享的,他們應該是“不可變的”(只讀),當有其他地方需要用到值對象時,可以將它的副本作爲參數傳遞。

1-6、service

DDD中每一個領域模型都應該具有屬性、狀態和行爲。

注意DDD中的領域模型與Java等語言中的POJO不同,POJO可以使用貧血模型來實現,但是DDD中每一個領域模型都應該包含於自身關係密切的行爲。比如一個Model叫做“用戶登錄信息”,那麼它自身就應該包含“修改密碼”這一行爲。

問題是領域中的一些行爲是無法映射到具體的對象中的,我們不能強行將其放入在某一個模型對象中,而將其單獨作爲一個方法又沒有地方,此時就需要服務。

例如,爲了從一個賬戶向另一個賬戶轉錢,這個功能應該放到轉出的賬戶還是在接收的賬戶中?放在這兩個中的哪一個也不對勁。當這樣的行爲從領域中被識別出來時,最佳實踐是將它聲明成一個服務。

服務的 3 個特徵:

  1. 服務執行的操作涉及一個領域概念,這個領域概念通常不屬於一個實體或者值對象。
  2. 被執行的操作涉及到領域中的其他的對象。
  3. 操作是無狀態的。

關於上面的第三點,服務是無狀態的,而對象是有狀態的。所謂狀態,就是對象的基本屬性。服務本身也是對象,但它卻沒有屬性(只有行爲),因此說是無狀態的。從便於理解的角度來說,可以把服務看做是utils。

從分層上來講,服務屬於Domain層。

1-7、模塊

爲了降低系統複雜性,通常的做法是將高關聯度的模型分組到一個模塊以提供儘可能大的內聚,同時消除耦合。

模塊應該由在功能上或者邏輯上屬於一體的元素構成,以保證內聚。

同時模塊應該具有定義好的接口,這些接口可以被其他的模塊訪問,同時將內部的模塊保護起來,不再對外暴露,組織外部的直接操作。

1-8、聚合(Aggregates)

聚合被看作是多個模型單元間的組合,它定義了模型的關係和邊界。

每個聚合都有一個根,根是一個實體,並且是唯一可被外界訪問的。

聚合的一個最重要的作用,是來處理數據變化的情況的。彼此之間有關聯(不管1:1、1:N、M:N)、一個變化會引發其他變化的一組模型,組合成一個聚合。

聚合的重要作用是保持數據一致性,並且可以強化不變量,因爲其他模型都參考聚合的根。要想改變其他對象,只能通過聚合的根去操作。根如果沒有了,那麼聚合中的其他對象也將不存在。

1-9、工廠

與設計模型中的工廠模型的作用相同。

爲複雜的模塊或者聚合提供創建對象的方法,用來代替簡單的構造器(依賴反轉)。

1-10、資源庫

從代碼角度來說,可以認爲資源庫就是一層緩存。

資源庫的目的是封裝所有獲取對象引用所需的邏輯。領域對象不需處理基礎設施,以得到領域中對其他對象的所需的引用。只需從資源庫中獲取它們,使得模型自身更加專注於業務邏輯,變更更加清晰。

是領域模型同需要保存的對象和它們的引用中解耦,由資源庫自行判斷是獲取已經存在的對象,還是從數據庫中獲取,或是乾脆調用工廠或者類似的辦法來直接創建。

二、DDD進階

// TODO

三、DDD與CQRS

// TODO

四、雜七雜八的一些思考

// TODO



作者:白花蛇草可樂
鏈接:https://www.jianshu.com/p/5ba3b3ab6e2c
來源:簡書
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章