我眼中的領域驅動設計

有幸參與了一些領域驅動的項目,讀了一些文章,也見識了一些不三不四的架構,感受對領域驅動有了更進一步的認識。因此今天跟大夥探討一下領域驅動設計,同時也對一些想要實踐領域驅動設計卻又無處下手,或者一些正在實踐卻又說不上領域驅動設計到底好在哪的朋友一些指引方向。固然對於」領域驅動設計」這個主題而言歷來不乏爭論,因此你們能夠在暢所欲言。php

爲何要使用領域驅動設計?mysql

從Eric Evans的《領域驅動設計:軟件核心複雜性應對之道》一書的書名就能夠看出這一方法論是爲了解決軟件核心複雜性的。也就是說軟件業務愈來愈複雜了,領域驅動設計可讓事情變得簡單。而實際狀況是:領域驅動設計的門檻很高,沒有很深厚的面向對象編碼能力幾乎不可能實踐成功。sql

這一說法是否自相矛盾呢?Martin Fowler在PoEAA一書中給了一個有力的解釋:數據庫

咱們把三層架構等除了領域驅動以外的架構方式均可以概括爲以數據爲中心的架構方式,在圖中是黑色的粗實線;編程

領域驅動設計在圖中是綠色的粗實線。設計模式

  • 當軟件在開發初期,以數據驅動的架構方式很是容易上手,可是隨着業務的增加和項目的推動,軟件開發和維護難度急劇升高。
  • 領域驅動設計則在項目初期就處在一個比較難以上手的位置,可是隨着業務的增加和項目的推動,軟件開發和維護難度平滑上升。

這幅圖形象的解釋了領域驅動設計和傳統的軟件架構模式二者在軟件開發過程當中解決複雜性之間的差別。緩存

領域驅動設計的核心是什麼?架構

顧名思義,領域驅動設計的核心是領域模型,這一方法論能夠通俗的理解爲先找到業務中的領域模型,以領域模型爲中心驅動項目的開發。而領域模型的設計精髓在於面向對象分析,在於對事物的抽象能力,一個領域驅動架構師必然是一個面向對象分析的大師。工具

在面向對象編程中講究封裝,講究設計低耦合,高內聚的類。而對於一個軟件工程來說,僅僅只靠類的設計是不夠的,咱們須要把緊密聯繫在一塊兒的業務設計爲一個領域模型,讓領域模型內部隱藏一些細節,這樣一來領域模型和領域模型之間的關係就會變得簡單。這一思想有效的下降了複雜的業務之間千絲萬縷的耦合關係。性能

下圖爲「以數據爲中心的架構模式」,表和表之間關係錯綜複雜:

下圖是「領域模型」:領域和領域之間只存在大粒度的接口和交互:

初期學習DDD的朋友必定不會錯過Eric Evans寫的《領域驅動設計:軟件核心複雜性應對之道》,這本書名氣很大,也是不少人入門領域驅動設計的首選讀物,這本書提到了領域驅動設計中的一些概念:Repository,Domain,ValueObject等。可是初學者有可能得出一個錯誤的結論:有人誤認爲項目架構中加入***Repository,***Domain,***ValueObject就變成了DDD架構。若是沒有悟出其精髓就在項目中加入這些概念,那充其量也不過是個三層架構;反之對於一個面向對象分析的高手而言,不使用這些概念也能夠實現領域驅動設計。

以IUserRepository這樣一個接口定義爲例:

1
2
3
4
5
6
7
public  interface  IUserRepository : IRepository<User>
{
     //What's this?
     List<Rule> GetRules(int id);
 
     //....
}

一個IUserRepository是一個Repository,他只能以User聚合根爲單位進行操做。方法List<Rule> GetRules(int id)將此Repository打回了原形,這再也不是一個Repository,這是一個DAL。

正確的實現方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public  class  User:AggregateRoot
{
     private  List<Rule> GetRules()
     {
         return  null;
     }
     
     public  void ApproveRequest(Request request)
     {
         var  rules = user.GetRules();
         //......
         //若是有權限就批准 
     }
}

這段代碼體現了User做爲一個領域模型,他擁有本身的職責和能力。

如何開始實踐領域驅動設計?

正如本文通篇所說,領域驅動設計講究的是領域模型的分析和對事物的抽象,歷來沒有提起過數據如何存取這個話題,言下之意在領域驅動設計中,咱們不關心過數據如何存取,怎麼樣寫linq效率高,使用懶加載仍是include,這些實現細節會將你帶入傳統的三層架構模式中。

在領域驅動設計中要先設計領域模型,接着寫Domain邏輯,至於數據庫,僅僅是用來存儲數據的工具。使用database first那不叫領域驅動設計,很明顯你先設計的表結構,因此應該叫數據庫驅動設計更爲準確。更不要引入數據庫獨有的技術,例如觸發器,存儲過程等。數據庫除了存儲數據外,其他一切邏輯都是Domain邏輯。

咱們不妨以你們都比較熟悉的醫院門診看病流程舉個例子,看看如何開始實踐領域驅動設計:

咱們暫且認爲一個門診看病流程就是一個完整的領域模型,此時你要忘掉數據庫,不要再想表結構如何設計,而是就這一領域模型進行抽象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public  class  OutPatientProcess:AggregateRoot
{
     public  Registration _registration { get;  private  set; } //掛號單
 
     private  List<Examination> _examinations;
     public  IReadOnlyList<Examination> Examinations => _examinations.AsReadOnly(); //化驗單
     public  Prescription Prescription { get;  private  set; } //處方
     public  DateTime ConsultaionTime { get;  private  set; } //接診時間
     public  Doctor Doctor { get;  private  set; } //接診醫師
 
     //開始一個門診治療過程
     public  void StartProcess(Registration registration)
     {
         _registration = registration;
 
         InquireSymptoms();
 
         WriteOutExamination();
 
         WritePrescription();
     }
 
     //詢問病人病情
     public  void InquireSymptoms()
     {
        
     }
 
     //開立化驗單
     private  void WriteOutExamination()
     {
         _examinations.Add( new  Examination());
     }
 
     //填寫處方
     private  void WritePrescription()
     {
         
     }
 
}

咱們暫且不討論這一模型是否符合真實場景,可是這個例子帶你邁入了領域驅動設計的第一步,同時這個例子也向你展現了軟件開發能夠不用先設計數據庫。當你寫好全部的Domain邏輯後再考慮把這個類持久化在數據庫中就行了。在我眼中,數據庫僅僅是一個保存數據的東西,不要把他過早的耦合在代碼中。這一強調了不少遍的觀點影響着你可否成功實踐DDD。

CQRS架構展望

話雖這樣說,可是既然你在使用關係數據庫,有人就會免不了跟你提起性能怎麼優化這樣的話題。這也是傳統ORM+關係數據庫實現領域驅動設計的硬傷,特別是當你的領域模型Scope設計過大,意味着Repository中的操做每次都要關聯一堆表出來,特別是有人設計數據喜歡遵照第N範式這種基本就沒轍了(沒有貶低遵照這些範式的意思,只是這樣設計的數據庫+ORM會產生較多關聯,相對應的設計爲表結構冗餘設計,有利於ORM提高性能),不得不說到了最後因爲數據庫的存儲性能問題,咱們又一次將數據庫歸入到了考慮範圍。

解決這一問題的方案是CQRS架構, Query端各類緩存和Nosql,順便把搜索引擎也用上,讓你的軟件飛奔起來。這一架構解耦了數據庫操做,你基本沒有機會跟數據庫打交道而且還解決了數據存儲的性能問題。

這一進化過程也解開了一些人的疑慮,爲何從剛開始寫代碼就開始學習各類設計模式,可是歷來沒有機會使用過?由於你所寫的代碼無時無刻不耦合着數據庫這一「毒瘤」,而數據庫操做做爲一種實現細節摻雜在你的代碼中,因此領域驅動設計爲此而生,你準備好了嗎?


原做者:http://blog.csdn.net/three_bird/article/details/51336834

相關文章
相關標籤/搜索