DDD領域驅動開發——概述

大家好,歡迎來到小蔣的技術圈。上次跟大家聊了IaaS、PaaS、SaaS都是什麼,估計大家還有印象。Iaas、Paas、SaaS實際上也是軟件行業進化的不同階段的產物。在這個進化的過程中,有一種設計思想在2004年的時候就被提出來了,也被作者寫成書出版。到現在爲止經過了18年了,但依然炙手可熱。而且逐漸變成了行業內微服務設計的標準實踐。他就是“領域驅動設計”,今天小蔣就準備和大家一起聊聊看。下面我們開始吧。

 

領域驅動設計並不是一個什麼新鮮玩意,它早在2004年的時候就被作者埃裏克·埃文斯發表了。書名爲Domain-Driven Design – Tacking Complexity in the Heart of Software。中文譯名爲領域驅動設計,清華出版社在2006年3月發佈譯本。大家也親切的稱呼它爲DDD,也就是它的英文單詞首字母。

 

我們來看一下Wikipedia中對領域驅動設計的定義:

領域驅動設計(英語:Domain-driven design,縮寫DDD)是一種通過將實現連接到持續進化的模型來滿足複雜需求的軟件開發方法。緊接着Wikipedia中寫到,領域驅動設計的前提是:

  • 把項目的主要重點放在覈心領域(core domain)和域邏輯
  • 把複雜的設計放在有界域(bounded context)的模型上
  • 發起一個創造性的合作之間的技術和領域專家以迭代地完善的概念模式,解決特定領域的問題

 

這就是Wiki中對DDD的定義,想詳細瞭解的夥伴,可以買這本書看看。

 

既然我們知道了DDD是一種軟件開發方法,或者說是軟件設計方法。那小蔣插一句說下我對設計的理解,歡迎大家拍磚。設計是把雙刃劍,沒有最好的,也沒有更好的,而是條條大路通羅馬。不設計和過度設計都是有問題的,恰到好處的設計纔是我們要追求的。不過要做到恰到好處非常困難。

 

所以埃裏克·埃文斯就推出了他的著作DDD。有一段時間DDD非常火爆,好多軟件公司都在搞。不過我要先說明DDD只是一種方法,談不上壓倒性的優勢,更不是完美無缺。切忌不可盲目崇拜,每種設計方法都有它特有的侷限性。Microsoft就曾經建議僅將它用於複雜領域中。雖然領域驅動設計提供了許多技術優勢,如可維護性。但領域驅動設計的系統可能會花費較高的成本。對於小型的系統,使用領域驅動設計可能會得不償失。

 

基礎

在“領域驅動設計”書中作者闡述了一些高層級的概念和實踐,比如通用語言,這意味着領域模型應該形成領域專家爲描述系統需求而提供的共同語言。我先給大家說一下,大家先有個概念就好。後面我再給家大傢俱體的分享一些真實的項目。

 

Entity-實體

一個不由自身屬性定義而是由標識線和它身份定義的對象。

 

Value Object-值對象

只包含元素屬性的不可變對象。

 

Service-領域服務

強調與其他對象的關係,只定義了可以爲客戶做什麼,不應該替代Entity和Value Object的所有行爲。

 

Module-模塊

一種表達機制,劃分代碼和概念。

 

Factory-工廠

對於那些需要創建特定域對象的方法應該委派給工廠對象,這樣可以更容易的替換實現。

 

Repository-資源庫

對於檢索特定域對象的方法應該委派給Repository對象,因爲這樣可以很容易地互換替代存儲的實現

 

Aggregate-聚合

由Root Entity 綁定在一起的對象的集合,也成爲聚合根。聚合根通過禁止外部對象保持其成員的引用來保證在聚合內進行的更改的 一致性。

 

Domain Event-領域事件

一個域對象定義了一個事件。域事件是域專家所關心的事件。

 

 

 

問題

 

說了一大堆虛的概念,到底DDD是個什麼玩意,究竟人們用DDD解決什麼具體問題?能不能更直觀一點?

 

我給大家找了一個“美團點評業務系統”的開發案例,他們在DDD實踐過程中的一個真實案例。那我們來看一下他們究竟要用DDD解決什麼樣的真實問題:

 

過度耦合

據美團工程師文彬和子維介紹,業務初期,我們的功能大都非常簡單,普通的CRUD就能滿足,此時系統是清晰的。隨着迭代的不斷演化,業務邏輯變得越來越複雜,我們的系統也越來越冗餘。模塊彼此關聯,誰都很難說清楚模塊具體功能意圖是啥。修改一個功能時,往往光回溯該功能需要的修改點就需要很長時間,更別提修改帶來的不可預知的負影響面。

 

 

 

訂單服務接口中提供了查詢、創建訂單相關的接口。也提供了訂單評價、支付、保險的接口。同時我們的表也是一個訂單大表,包含了非常多字段。在我們維護代碼時,牽一髮而動全身,很可能只是想改一下評價相關的功能,卻影響到了創建訂單的核心路徑。雖然我們可以通過測試保證功能完備性,但當我們在訂單領域有大量需求同時並行開發時,改動重疊、惡性循環、疲於奔命修改各種問題。

 

後來我們總結,上述問題,歸根到底在於系統架構不清晰,劃分出來的模塊內聚度低、高耦合。

 

我們嘗試過一種解決方案,就是按照演進式設計的理論,讓系統的設計隨着系統實現的增長而增長。我們不需要提前做設計,就讓系統伴隨業務成長而演進,這其實是一種敏捷實踐。後來發現效果很差,當然也可能是我們內部的問題。我們承認敏捷實踐中的重構、測試驅動設計及持續集成可以對付各種混亂問題。重構,保持行爲不變的代碼改善清除了不協調的局部設計。測試驅動設計,確保對系統的更改不會導致系統丟失或破壞現有的功能。持續集成,則爲團隊提供了同一個代碼庫。

 

在這三種實踐中,重構是克服演進式設計中大雜燴問題的主力,通過在單獨的類及方法級別上做一系列小步重構來完成。我們可以很容易重構出一個獨立的類來放某些通用的邏輯,但是你會發現你很難給它一個業務上的含義,只能給予一個技術維度的含義。這會帶來什麼問題呢?新同學並不總是知道對通用邏輯的改動或獲取來之該類。顯然,制定項目規範並不是好的idea。我們又聞到了代碼即將腐敗的味道。

 

事實上,你可能意識到問題之所在。在解決現實問題時,我們會將問題映射到腦海中的概念模型,在模型中解決問題,再將解決方案轉爲實際的代碼。上述問題在於我們解決了設計到代碼之間的重構,但提煉出來的設計模型,並不具有實際的業務含義,這就導致在開發新需求時,其他同學並不能很自然地將業務問題映射到該設計模型。設計似乎變成了重構者的自娛自樂,代碼繼續腐敗,重構重構……無休止的循環。

 

我們要解決的實際問題就是代碼的設計模型與業務模型不匹配這個問題,導致的重構工作無休止的循環。後來我們選擇了DDD。

 

爲什麼選擇DDD

解決複雜的大規模軟件的武器可以被粗略地歸爲三類:分治、抽象和知識。

 

分治,把問題空間分割爲規模更小且易於處理的若干子問題。分割後的問題需要足夠小,以便一個人單槍匹馬就能解決他們;其次,必須考慮如何將分割的各個部分裝配爲整體。分割得越合理越容易理解,在裝配成整體時,所需跟蹤的細節也就越少。即更容易設計各個部分的協作方式。評判什麼是分治得好,即高內聚低耦合。

 

抽象,使用抽象能夠精簡問題空間,而且問題越小越容易理解。舉個例子,從北京到上海出差,可以先理解爲使用交通工具前往,但不需要一開始就想清楚到底是高鐵還是飛機,以及乘坐他們需要注意什麼。

 

DDD提供了這樣的知識手段,讓我們知道如何抽象出限界上下文以及如何去分治。DDD的核心訴求就是將業務架構映射到系統架構上,在響應業務變化調整業務架構時,也隨之變化系統架構。

 

這就是“美團點評業務系統”他們用DDD來解決的真實問題。

 

總結

所以說,DDD實際上就是一整套的設計方法論,和複雜場景下如何開展軟件開發的一種工具。爲的就是幫助我們建立一整套的邏輯思維方式,指導我們解決複雜場景下的的軟件建設。

 

小蔣想跟大家分享的是我們是否關注設計本身,不管是什麼流派的設計,有設計就是好的。經歷了這麼多年的開發,領悟到設計真的很重要啊,不設計的代碼今天不死也是拖到明天去死。不管我們在團隊裏待多久,不能給未來的兄弟挖坑啊!

 

好的,以上就是今天的分享。下次我們來探討一下DDD中提到的領域模型具體是個什麼東西。好,我們下次見。

 

 

音頻地址:https://download.csdn.net/download/wei_wei10/12091662

(審覈通過,就能下載了)

 

發佈了32 篇原創文章 · 獲贊 2 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章