.NET領域驅動設計—實踐(穿過迷霧走向光明)

閱讀目錄html

  • 開篇介紹
  • 1.1示例介紹 (OnlineExamination在線考試系統介紹)
  • 1.2分析、建模 (對真實業務進行分析、模型化)
    • 1.2.1 用例分析 (提取系統的全部功能需求)
  • 1.3系統設計、建模 (技術化業務模型)
    • 1.3.1 枚舉類型的使用 (別讓枚舉類型成爲數值型對象)
    • 1.3.2 基礎數據、業務數據 (顯示實體和隱式過程)
    • 1.3.3 模型在數據庫中的主外鍵關聯問題 (面向對象模型與關係模型的自然抗阻)
    • 1.3.4 角色、類型 (區分類型與面向對象概念)
    • 1.3.5 名詞、動詞、隱、顯、抽象、具體 模型建立技巧 (面向對象分析技巧)
    • 1.3.6 永遠都不要去假設你的模型 (28原則)
  • 1.4重構模型 (規則引擎、精簡模型、模型擴展性)
    • 1.4.1 規則引擎 (複雜業務系統的一個重要分支)
    • 1.4.2 精簡模型 (聚焦系統核心,以業務模型爲主)
    • 1.4.3 模型擴展性 (運行模式、常規法來設計面向對象)
  • 1.5系統架構設計、DDD分層架構 
    • 1.5.1 傳統分層架構 (沒法知足大規模業務系統而逐漸被淘汰)
    • 1.5.2 DDD充血型架構 (較豐滿的業務模型)
  • 1.6數據存儲設計 
    • 1.6.1 模型與關係數據之間的平衡 (分析、設計、架構的重要體現)

開篇介紹

在開始這篇富有某種奇妙感受的文章之旅時咱們先短暫的討論一下關於軟件開發方法論的簡要:程序員

縱觀軟件開發方法論,從瀑布模型、螺旋模型、RUP(統一軟件開發過程)、XP(極限編程)、Agile(敏捷開發)一路走來,他們的好他們的美,我想接觸過的人都會口口稱讚,都是大師們一身的經驗結晶最後沉澱爲專業的技術方向、技術領域,帶領咱們軟件開發者們永無止境的前進,目擊一場又一場的美景一桌又一桌盛宴。他們在不斷的開闢新的領域,稱爲偉大的科學家一點都不爲過。數據庫

可是爲何這麼多方法論都沒有能在企業中大面積的普及和使用或者說未能取得理想的效果呢,難道說是咱們都不會嗎?固然不是,我想咱們程序員都是很聰明並且很富有創造性思惟的人羣,咱們勇於改變現狀追求真理,可是時間過去了不少,咱們彷佛都沒有真正解決複雜軟件的設計問題,咱們參考不少書籍,數不勝數,擴展類、模式類、模型類太多太多,可是問題的核心始終未能觸碰到,在黑暗中無數的摔倒都未能找到突破口。爲何DDD(領域驅動設計)能被咱們接受而且願意花時間花精力去學習去實踐它,由於它發現了複雜軟件設計問題的核心解決方法(Model Driven Develop 模型驅動開發),聚焦複雜系統的核心,而且有一套完整的框架、流程指導咱們進行相關DDD的設計、開發工做 。編程

在DDD未出如今咱們面前時,咱們碰見覆雜且龐大的業務系統的時候,會手忙腳亂的亂折騰,會發現根本沒法拿下這麼一個龐大的Monster,最後項目就算僥倖成功也只是依靠我的力量英雄主義般的在獨自一人戰鬥,加班、熬夜精神極度集中,燃燒生命最後就算能取得成功,但每每系統最後仍是這裏出錯那裏出錯,甚至還會漏掉什麼功能沒作,這算是一個常態,不足爲奇了。設計模式

如今咱們有辦法改變這種局面,團隊是幹什麼的?團隊不是樣子好看,三個臭皮匠勝過諸葛亮,只有一塊兒參與系統的分析、設計才能最大化的保證需求的穩定性,最起碼作到一塊兒評審分析方案。別說我太理想化,難道這不是咱們共同的期盼嗎?緩存

總而言之使用DDD的朋友都能感覺到它的不同,愛惜生命的朋友請開始和我一塊兒DDD(領域驅動設計)之旅吧!安全

1.1】示例介紹OnlineExaminationSystem

通過前面兩篇文章的講解,咱們算是對DDD有了一個初步的認識,對它的概念它所提倡的開發原則開發思想有了一個基本的瞭解。任何方法論都要能被技術化落實到代碼上才行,要能真正的爲咱們解決問題,因此咱們這裏使用DDD進行一個完整的系統分析、設計、開發來驗證它確實如所說的那麼好,接受一個新的東西每每須要一個時間過程,因此文章可能會有點長,基本上都是一些理論;服務器

前面兩篇文章的地址:網絡

1.NET領域驅動設計—初嘗(一:疑問、模式、原則、工具、過程、框架、實踐)數據結構

2.NET領域驅動設計—初嘗(二:疑問、模式、原則、工具、過程、框架、實踐)

初次接觸DDD的朋友能夠先閱讀一下上面兩篇文章,算是有一個宏觀的理解,DDD是如何顛覆傳統的設計方法的。

項目背景介紹:

爲了保證明踐項目的全面性,這裏挑選了我一直比較關注的教育行業信息化的一塊【在線考試系統】做爲實踐的項目,使用了C/S、B/S混合型的系統結構,系統的業務範圍主要是一個面向學校的學生在線考試系統。學生經過客戶端(C/S)進行在線答卷,答完卷後再經過遠程服務進行答題數據提交。老師經過後臺(B/S)進行試卷的打分,最後得出全部學生的成績數據而且在成績公告欄中顯示排名。(固然因爲時間關係,示例代碼可能推遲一點時間發佈;)

擴展:

教育類系統都存在一個問題就是【老師】、【學生】、【家長】三者之間是沒有任何信息化聯繫;對於像考試類的管理都沒有任何方式告知家長學生的成績狀況,包括最近的成績趨勢,還有就是學生的總體對比度等等。21世紀什麼最重要?人才;如今的家長都迫切的想知道每一天學生在學校的狀況。因此對於這樣的須要頗有價值去分析,去實踐起來,固然前提是教育須要進行改革才行。

1.1圖

(注:查看大圖)

這是一個形象的需求思惟導圖,很形象的描述了咱們系統的大概功能,最重要的是能表達真實的業務場景,這也是模型驅動開發的首要思想。用專業的領域驅動設計思想來描述的話上圖那就是(解釋性模型)帶來的效果,若是咱們用很抽象的程序專業圖形來表達業務需求很難說明問題,因此咱們在前期與業務人員溝通大概需求時基本上給出咱們人類天生就能理解的圖形化表示,能夠視它爲「解釋性模型」;固然因爲時間關係並不會完成上面全部的需求;

在以【School DataCenter】爲中心的【Student】學生【Teacher】教師【Parents】家長【Admin】管理員【CEO】最高執行人,各角色分別處理一個業務環節上的不一樣操做;

這只是簡單的項目介紹,目前大概就這些基本的需求,後面咱們會進行詳細的系統分析。業務都具備發散性特色,看似簡單最後仍是會出現不少需求問題,這也符合咱們所須要的要求。從上圖中咱們能夠很簡單的就明白系統的基本功能,可是畢竟是一個簡單的需求草圖,只是咱們腦子裏的一個雛形,咱們須要把它落實到真正的項目中去,這個過程很是的不容易,惟一能正確穿過迷霧的方法是對照模型進行分析、設計。

1.2】系統分析、建模

上小節中咱們基本瞭解到了系統主要有哪些功能而已,這一節咱們將詳細的對系統進行業務分析,固然主要是爲了突出領域建模的重要性,關鍵是它能爲咱們帶來什麼樣的好處。當咱們逐漸按照DDD的方式來設計系統的時候,你會發現一切都很順利並且很OO,咱們徹底能夠使用OOA\OOD的方式且沒有任何干擾的進行系統開發。

雖說這是一個比較簡單的在線考試系統,可是若是要把它作好實際上是蠻龐大的,會和不少其餘的系統聯繫,因此這裏不會考慮太多的需求;

(這個項目自己的目的是爲了演示DDD的設計、開發、架構,因此在需求上不會太難;)

1.2.1】用例分析

經過用例來分析系統中的基本功能調用,這部分的調用是侷限於外部的調用,畢竟是外部調用驅動內部調用;全部用例是捕獲外部調用的功能圖,具體的內部調用看具體的狀況而定;

【學生用例】

學生用例主要就是進行系統考試,從【登陸系統】【進入考場】而後【領取試卷】等待【答卷開始】通知,在限定的時間內進行【答題】;等時間到了以後系統自動進行【試卷提交】,固然本身也能夠【提早交卷(條件是必須離考試結束時間10分鐘以內)】

2.1圖

【教師用例】

選擇分配給本身的試卷進行改卷(對於【選擇題】【判斷題】能夠實現自動化的改卷;)。

教師的用例主要是【登陸系統】,每一個老師所要批改的試卷都是由【管理人員】進行分配的,因此這裏直接進入到本身所要批改的【試卷列表】便可,而後選擇某一個【試卷】進行批改,批改的【參照答案】應該是事先就已經準備好的,由N多位老師共同完成的一個標準的答案;

2.2圖

【管理者用例】

管理者只須要將全部的試卷分配給參與改試卷的老師們,這裏目前按照【考場進行分配】

一個考場須要進行N門科的考試,會有不一樣的老師站考,可是老師跟科目在這個時候沒有任何關係;等考完試以後,咱們如何保證公平公正的對批改試卷老師的分配,因爲考試的成績最後會對老師的晉升、年末獎金等等;因此這裏我先按照考場+科目進行分配,只有這樣才能保證最小的誤差;考場裏面的試卷都是通過隨機安排的學生提交的,因此不會存在問題;

2.3圖

【家長用例】

這裏咱們不考慮太多家長的用例,基本的【短信通知】學生的考試成績和在班級、年級組的排名,若是複雜一點的能夠有一個圖形的分析,家長能夠進入考試系統進行分數的查看或者與老師之間的線上交流;

2.4圖

【最高執行人】

這類人的功能咱們這裏只包括一個學生成績的【圖形統計】

2.5圖

【系統分析技巧】

其實咱們大部分程序員都不太願意與需求的提出人進行多溝通,認爲他們提出的東西可能很不太理想;可是在DDD中,提倡頭腦風暴似的交流討論領域問題,過程是須要敏捷的、迭代的;從文章的一開始的項目介紹到用例分析,我想咱們都會看到不少被我標記爲「【***】」這樣格式的文本關鍵字,其實這個過程很重要很重要,這是咱們與領域專家在溝通需求的時候須要積累、總結的領域語言,咱們只有完全的弄明白這些關鍵點才能爲咱們的分析打下一個初步的基礎,這樣才能循環漸進的進行迭代。

需求其實就是隱藏在咱們交流的描述當中,要習慣性的把一些關鍵的字\詞先抽出來記錄下來,過後本身在慢慢的分析琢磨;你會發現很神奇的是這些關鍵部分偏偏是用例的重點也是下一步領域模型設計的依據;從專業的領域驅動設計角度講這些關鍵字都是領域通用語言的一部分,是咱們進行交流的模型語言;咱們在進行項目交流的時候會對一些口頭描述出來的需求產生二義性,可是咱們若是使用領域模型進行交流的話就不會存在二義性,需求永遠都是等價的在咱們之間傳遞;

1.3】系統設計、建模

按照上圖中的用例分析咱們這裏須要對這些用例進行面向對象設計,也就是建立領域模型,得出領域模型以後咱們的系統雛形就出來了;

看過前面兩篇文章的朋友就會對建立領域模型有點熟悉,建立領域模型有一個很好的設計思想就是(四色原型模式),它能夠幫助咱們很好的完善領域模型,找出核心領域模型以後就很容易進行模型邊界修飾逐漸的完善,仍是那句話:須要敏捷、迭代的進行構造;沒有一次就能使用的領域模型,中間的過程是省不了的,須要不斷的重構、提煉才能使模型最後精簡而富有豐富的領域概念。

【詳細的四色原型模式後面會有專門的文章來深刻的講解,這裏你們只須要知道類型原型就好了,有興趣的朋友能夠本身百度相關文章】

【領域模型】

若是得出領域模型,須要咱們對上面的用例進行細緻的分析而且逐漸的勾畫出邊框,再慢慢的修飾得出一個基本的模型圖,可是咱們要知道一個有血有肉的模型是須要不斷的去呵護它才行的;咱們須要不斷的重構,不斷的剔除一些障礙留最真實的模型,最後領域模型將是本次項目的一筆豐富的財富;

咱們一個一個用例來過,首要的是學生的用例;

【學生用例模型】

首先是【學生】主體模型,這裏咱們能夠將【四色原型模式】的思想引進來了,你會發現你忽然很會設計模型了(呵呵,開個玩笑!);對照四色原型咱們知道【學生】應該是有類型之分的,那麼學生的類型的分類屬性是按照什麼來呢?好比是性別(男、女)、職務(班長、組長)、成績優差(優、良好、差)等,決定要使用什麼屬性做爲咱們的分類標準須要看系統的需求來定了,可是最起碼四色原型模式告訴你你缺乏某種分類;那麼咱們這裏只須要按照學生的性別進行一個基本的類型分類就好了;

3.1圖

有了一個基本的核心點以後咱們後面的思路其實基本上就會好延伸下去,咱們繼續分析;

看到了【Student】發起的第一個用例是登陸系統,既然是登陸系統那麼確定是要用戶名、密碼的,而且通常性的約束是要控制是否啓用、禁用該學生,好比學生不在本所學校了,不可能將學生的信息刪除的,它牽涉到不少其餘的系統和業務數據;因此咱們這裏又增長了三個基本的屬性,「用戶名」、「密碼」、「啓用禁用」標誌;

3.2圖

咱們加上了三個基本屬性;好像還差點什麼?學生的基本信息彷佛沒有,那麼咱們加上關於學生的基本屬性(學生的姓名、年齡、學號);

3.3圖

那麼學生的基本信息算是沒問題了,咱們繼續沿着用例分析;【進入考場】用例是學生選擇相應的考場而後進入,在真實的線下考試當中每一個考生在考試的時候都會有一個考號,憑考號進入考場纔對;可是咱們這裏不能將【考號】直接放入到學生的基本信息當中去,爲何呢?由於一個學生在學校期間會經歷N場考試;因此咱們須要獨立的模型來表示學生與考號之間的關係;

可是咱們本身分析一下,考號的生成是有必定的規律的,它通常都是跟學生所在的考場信息掛鉤的,好比:0320,應該是第3考場第20座位;咱們在反推一下,其實考號跟學生在考試以前的某一個時間段他們之間是沒有任何聯繫的,也是說應該是先安排考場而後再根據考場中的座位再進行考號的生成,並且每一個時間段的考試都是一個年級組的,要麼初一的全部學生考試,要麼初二的全部學生考試因此當考號生成後將隨機的映射到每一個學生身上;固然也有另外狀況就是每次考試時間週期內會有多個年級組進行考試,可是這個時候考場中的考生是如何設置的就要看具體的需求而定了,因爲話題比較大因此這裏就不涉及了。這裏就當它是獨立年級組進行考試;

考場就是班級,在考試的時候全部的班級都將會變成考場的屬性,好比本來是「三五班」可是考試的時候就變成了03考場了,因此咱們須要對班級進行建模,有了班級以後才能慢慢的設計考場的模型。

3.4圖

班級的類型存在多個屬性的分類,」Class_UseType」是表示班級的使用狀況,好比班級在裝修的時候可能就是Close狀態,平時都是Open狀態;另外還存在着一個班級是被哪一個年級使用的,好比是初一仍是初二,或者是高1、高二等等;咱們須要明白的是,類型是事前約定好的,類型是肯定的模型,它與可維護基礎數據是不一樣的,後面會有專門的小結作詳細的講解;

這裏的Class_UseType模型是表明當前班級的使用狀況,班級會存在「使用」、「停用」狀況,好比某個班級在裝修或者其餘的問題;這中分類徹底能夠用枚舉類型進行表示,可是Class_GradeType班級所屬年級,表明當前班級是屬於哪一個年級組的,這個屬性在後面確定是會用到的,好比要將全部的初一班級做爲本次考試的場地,這個時候就用到了;在Class中有一個PewNumber的屬性是表示當前班級的座位數,通常考試都是50%的座位是在考試中使用;

本打算把全部的用例模型的分析都寫出來的,可是這裏因爲時間關係就不一步一步分析說明了,對於學生考試的這部分模型圖差很少是這樣子的:

3.5圖

麻雀雖小五臟俱全,儘管這裏截圖不是所有的(有所有源碼供下載),可是能說明一個問題:利用領域驅動設計開發軟件確實對設計技術要求較高,圖中藍色的都是潛在的深層模型,而灰色的是咱們最容易發現的表層模型。咱們往常進行面向對象設計的時候都是很容易的發現一些表層的模型好比:人、車等一些實物,可是很難發現一些潛在的模型好比:一次婚紗拍攝場地,一次吃飯過程等等,這些潛在的模型都是核心業務模型,因此當咱們使用DDD進行軟件開發的時候不知不覺的就會讓你對業務清晰度要求變高了,不會存在含含糊糊的業務需求;

1.3.1】枚舉類型的使用

在通常的框架型項目中都會使用枚舉來表達某些概念上的一個序列,枚舉是約定的表達,枚舉只限定在它的區間取值;好比咱們都愛寫寫框架、組件被別人使用,你們最熟悉的莫過於ORM框架了,ORM框架裏面都會有本框架所能支持的數據庫種類枚舉,使用枚舉來約定只能使用這區間的值,沒別的選擇;

可是枚舉咱們也能夠用在DDD上,在以往的業務性系統中不多能看見和業務相關的枚舉,都是功能性的,好比寫Log、Email的等等;而後這裏咱們須要把它作爲在DDD中的核心對象模型來使用,好比用戶的登陸類型、支付方式,前提是已經約定好的;

枚舉類型與基礎數據會存着混淆,我到底使用枚舉仍是基礎數據結構,這裏很簡單的區分就是看你的基礎數據往後是否須要不斷的維護擴展;好比學校的班級往後確定是須要不斷的修改或者添加新的班級,而學校平常考試範圍基本上都是鎖定在那幾門,中國的但是科目就那幾種徹底能夠直接定義枚舉約定;在編碼階段很簡單的進行枚舉出值,如:FieldExamination.Subject==Subject.English,將本場考試爲English做爲條件;一般枚舉類型都是做爲值類型出如今DDD中;

1.3.2】基礎數據、業務數據

【基礎數據】

基礎數據是系統在上線運行以前就已經維護進去的啓動數據,好比咱們這裏的【班級】、【學生】、【教師】等等,用來支撐系統運行的必備數據,這些數據廣泛存在一個特色那就是「實體」數據,那麼咱們如何判定是否是一個基礎數據,按照分析模式「四色原型模式」,中的「參與者、地點、物品(party, place, or thing 」原型咱們能夠把它想象成是能夠進行任何獨立使用的獨立單位對象,叫作「基礎數據」

【業務數據】

業務數據很明顯的特色就是發生在某個時間段上的事情,好比咱們的一次購物、一次郊遊、一次拍照等等,都是創建在基礎數據之上的,從現實角度去分析人物事情的發生都是須要人的介入,當人介入以後將發生對某物的操做,好比:強強在2013年6月1日參加了學校舉辦的六一兒童節活動,這裏的【強強】是基礎數據而發生的這一整個事件爲業務數據,也是「四色原型模式」中的核心原型,業務數據牽動着基礎數據,關聯着彼此不相關的基礎數據;一樣咱們使用「四色原型模式」中的「某個時間段的間隔(moment—interval)」原型咱們就能夠很明顯的找出什麼是業務數據數據;

這裏我順便扯一下「四色原型模式」並非Martin Flower大師所寫的分析模式,他寫的應該歸類於「業務原型」,而這裏的「四色原型」是peter code的傑做,這裏就很少講了,畢竟我對四色原型也只是初步的瞭解;【對四色原型有興趣的朋友能夠直接看《彩色UML建模》Peter Code 大師的書】

基礎數據與業務數據之間的關聯須要很當心,咱們要衡量好之間的關係。當咱們識別出基礎數據以後在首次進行建模的時候爲了合理的表達領域模型的完整性會將其關聯的很緊密,在團隊中進行交流評審的時候都是頗有幫助的,到了後面重構階段必定會將龐大的蜘蛛網合理的拆開造成精簡的聚合模型;

本節想強調的是正確的識別出領域中的「基礎數據」和「業務數據」,讓後將其合理的關聯來表達領域模型;

1.3.3】模型在數據庫中的主外鍵關聯問題

當模型落實到代碼上的時候咱們就要考慮如何將模型在關係型數據庫中存儲的問題,固然你能夠存放在任何地方,可是不一樣的數據存儲方式會對你的模型有必定程度的影響或者說會影響你建模的細節思路,這是須要平衡的;有人會說:「模型的建立應該徹底不用去考慮到底如何持久化」,難道真的是這樣的嗎?我經過實踐證實問題偏偏相反,當你在建模的時候若是不懂的技術實現那麼就會產生像DDD書中所說的分析與設計之間的裂縫,分析人員分析出來的模型根本沒法在真實的技術環境下實現;難道你還會說:」分析的時候徹底不用去考慮到底如何實現「,這就是DDD所說的複雜軟件開發問題所在;

在目前狀況下廣泛認爲分析人員大於程序員,他們占主導地位,想固然的去搜集業務而後交給你實現,當他讓你實現一個很彆扭的功能的時候其實你徹底能夠用本身的專業意見來改善的很平滑,可是因爲工做職責的不一樣他們並非很懂的技術實現的細節因此問題就在這裏;

【這不是我結論,在《領域驅動設計.軟件核心複雜性應對之道》一書中,Eric Evans 是這麼定義的,詳見書中第7章;問題確實如此;】

這裏咱們只討論面向關係型數據庫的存儲方式;聚合是一類實體的集合,會有一個「帶頭」的實體也就是聚合根,咱們對它的操做須要很當心,好比:當你插入一個聚合根時會把聚合根所涉及的一些附屬模型都插入,這個時候就是錯誤的;這個時候若是沒有很好的數據訪問組件來支持的話咱們很難保證數據的一致性,後面我會專門寫一個系列針對Microsoft.EntityFramework的文章,由於目前.NET平臺穩定的實體框架就屬實體框架EF了;

按道理咱們的聚合是一個帶有根的實體集,他們被邏輯劃分到一個業務範圍中,好比【FieldExamination】每場考試聚合,當咱們查詢有關一場考試信息的時候會關聯出它所附屬的一些其餘信息,這是查詢沒有問題,可是當咱們進行刪除、更新、添加的時候問題沒有那麼簡單了,固然是能夠避免了這裏跟你們分享一下須要注意的地方;

public class FieldExamination:EntityRoot

{

   public string FId{get;set;}

   public Datetime BeginTime{get;}

   public Datetime ProcessTime{get;}

   //獲取本場考試的試卷

   public  GetCurrentExBook(string stuId){//……}

   //本場考試的負責人

   public Employee  Principal{get;}

   public Subject  CurrentSubject{get;}

}

public class FieldExaminationRepostiroy:Repository<FieldExamination>

{

  public FieldExamination GetById(string id){//……}

}

上述FieldExamination實體沒有任何問題,恩 看起來是沒有問題;當咱們對數據庫進行查詢的時候是沒問題的,會順利的獲得FieldExamination實體確實很方便,這也是DDD的精神所在;可是當咱們進行對象的插入的時候問題來了,一般咱們對實體進行建立的時候是會經過一個專門的Factory來建立,這個對象是一個完整的,包含了基本的屬性信息,好比這裏的FieldExamination實體建立的時候是確定要知道它的負責人是誰而且本場考試的科目是什麼,咱們會對相關的屬性進行賦值,那麼這個時候進行插入的時候就會將相關的屬性插入到屬性所要持久化的表中去,這裏也就是會將Principal屬性插入到Employee表中去,那麼就是錯誤的,一樣其餘的屬性都是這種狀況;

那麼到底如何解決,其實就是經過「標量屬性」來解決,這個時候實體會增長一些屬性所對應的「屬性字段」如:

public class FieldExamination:EntityRoot

{

  public string FId{get;set;}

  public Datetime BeginTime{get;}

  public Datetime ProcessTime{get;}

  //獲取本場考試的試卷

  public GetCurrentExBook(string stuId){//……}

  //本場考試的負責人

  public int PrincipalId{get;}//查詢的時候這個屬性不須要關心

  public Employee Principal{get;}

  public int CurrentSubjectId{get;}//查詢的時候這個屬性不須要關心

  public Subject CurrentSubject{get;}

}

也就是說進行插入、更新的時候只須要使用「標量」屬性來更新插入便可,由於不須要涉及到對其餘對象的操做;

1.3.4】角色、類型

模型的角色、類型,我想你們應該多少有點了解的,如:訂單有訂單的類型,考試有考試的類型,這也是「四色原型」中所講「Role」原型;無論是從什麼維度進行分類、分角色都是有必要的,當你缺乏角色分類是應該提醒本身可能你漏掉了什麼,由於據以往經驗告訴咱們「角色、分類「確定是會用到的;

在咱們前面對學生用例進行分析的時候不少地方都是須要角色、類型,使其看起來很合理;

1.3.5】名詞、動詞、隱、顯、抽象、具體  模型建立技巧

這裏給你們總結一下系統分析的一些基本的技巧,亂七八糟的理論這裏就不扯了,有興趣的朋友能夠看專門的書籍,這裏是比較簡單能直接用的技巧;

【名詞】—>【顯】

當咱們和業務人員進行業務溝通的時候咱們會聽多不少【業務名詞】,首次談話業務名詞對咱們來講可能比較難以理解尤爲是複雜的業務領域,固然會隨着屢次的溝通逐漸的理解而且得出比較合理的領域通用語言;在不少時候【名詞】法能很快的捕獲到最直接的【顯】層模型,好比在本示例中咱們能很快而且很準確的將【名詞】中如:學生、教師、班級等等這些實體模型,這些實體信息最容易被人理解和接受;當咱們和業務專家進行溝通業務的時候不要光聽他們講要把他們講的每個業務環節中的涉及到的業務名詞記下來而後本身再過一遍對不懂的必定不要假設什麼什麼,必定要虛心的向他們討教哪怕他們真的煩了那也沒辦法,由於這是工做;

【動詞】—>【隱】

一樣和業務專家進行溝通的時候會有不少【過程】、【動做】等等這些名詞出現,好比進行【一場考試】,那麼就會涉及到對動詞的抽象了,顯然【一次考試】是」四色原型「中的」moment—Intervel「,對於這樣捕獲下來的模型是具備很強的業務性的,這樣細心的分析慢慢的挖掘這些潛在的動詞模型,也有可能動詞模型會覆蓋動詞模型,好比一次考試可能已經包含老師站考記錄,一次考試會隱藏諸如這些潛在的模型;

具體的模型是咱們一眼就能看穿的實體模型,是一些人、物,而咱們很難發現是過程模型也就是複雜的業務流程模型,在某個業務環節下要涉及到不少業務模型,最重要的也就是【發生了什麼事情】,只有發生了事情才能將人、物關聯起來;

【具體=名詞、顯】—>【抽象=動詞、隱】

被咱們意識能直接識別出來的一般都在咱們的知識水平面上,只有具體的東西才能支撐抽象的事物;若是沒有人會有【訂單】嗎,若是沒有貨物會有【配送】嗎,若是沒有CUP、內存會有進程嗎;識別出顯示的模型固然是最直接的引導方式;

3.6圖

上圖是一隻抽象的魚(abstract fish),若是沒有具體的骨架(Concrate Frame)它是不會有形狀的,想要獲得這隻魚必須得有骨架模型幫你支撐起來才行;一樣的道理,咱們在分析系統的時候也同樣的,須要識別出具體的事物而後才能穩健的抽象出模型;(固然並非絕對的,你也能夠進行抽象優先,可是我想前提是你腦子裏已經有具體的事物了)

1.3.6】永遠都不要去假設你的模型

咱們之前常常會犯一個錯誤,就是常常去假設系統能提供什麼功能,好比咱們在分析一個系統的時候老是喜歡假設它應該具有什麼功能,那麼這些功能真的能替你畫龍點睛呢,仍是在多此一舉;廣泛現象是分析人員在進行分析的時候都沒有必定程度的搞懂需求的真正目的是什麼,每個需求的背後是價值驅動的,必定要讓業務人員告知你一二,這樣能夠方便你自主能動的去觸類旁通,而不是他說「告訴你也不懂」你就很差意思的結束了,千萬不;若是他不告訴你清楚的需求你就沒法畫龍點睛,沒法進行知識消化也就談不上模型重構了;切忌不要假設咱們應該作什麼功能,咱們全部的功能需求都是業務人員須要的,或者是潛在須要的,這個潛在是他跟你講了需求背後的價值才能設計;

1.4】重構模型(規則引擎、精簡模型、模型擴展性)

這個過程是設計階段最重要的核心過程;咱們一直都認爲本身的設計能力不錯,就是一直沒找到合適的地方運用,至少在數據庫驅動的軟件開發中你是用不上什麼設計思想的,邏輯都在數據庫的存儲過程裏面;你的面向對象設計如何的了得不,你的面向接口編程運用的如何的出神入化,你的諸多好的模式都用不上;可是在DDD的(重構模型)階段你將能夠大展身手,用專業術語來講這個階段是(設計模式)介入的階段,經過深刻的挖掘業務潛在的變化點,經過模式將變化點抽象出來,將變化點隔離在系統的外部;

1.4.1】規則引擎

這裏隨便提一下(規則引擎)的相關概念;

咱們都知道面向對象設計思想真的很神奇,是那些軟件大師、科學家研究出來的,在倡導進行DDD驅動的時候咱們用面向對象的思想來抽象領域模型,用C#、JAVA之類的面嚮對象語言來實現等價的模型代碼,可是在一個系統中對象只是一種模型;在DDD的分層架構中的Business Layer中咱們放置的都是Domain相關的模型,在這一層也就是最核心的一層裏,咱們用對象來表示全部的業務模型,就比如最小粒度的細胞同樣;可是這些模型沒有一個點將他們穿起來造成一個總體性,好比在業務系統中都存在着業務流程,那麼流程須要使用到的Domain如何被模型化,其實也就是工做流(Workflow),工做流模型用來抽象全部的業務流程,也就是咱們DDD分層架構中的Application Layer中的元素,全部的調用進入到Application Layer 層以前都是有前後順序的,好比審批流程,要先提交審批的相關單據信息,而後才能到達【審批 Public bool Auditing(Forminfo info){//審批邏輯}】的環節,這樣的流程須要相關的框架支持才行;咱們有工做流引擎來支撐,可是DDD的核心元素模型業務規則(Business specification)還沒法很好的在咱們系統中實現;這自己就是靜態語言的一個問題,靜態語言的全部邏輯在編譯時就已經肯定,無論你是直譯式仍是中間式的編譯過程,自己語言的設計就是這種靜態思想;

咱們你們對Js多多少少確定是比較熟悉的,它自己就是一個動態的語言,咱們且不問是不是腳本語言;它能很好的解決在運行時動態的改變對象的全部屬性、行爲;這一點在很大程度上便於咱們對規則的設計,因爲規則是動態變化的,全部動態語言是用來開發規則引擎的一個好的工具;固然並非規則引擎就是這點東西;咱們能夠適當的研究一下Ruby、Python之類的動態面嚮對象語言,對規則的設計是頗有好處的;從性能角度將JS確定是不能在服務器端使用的,能夠使用Python編寫規則引擎,固然還有不少函數式語言都很不錯;在很大程度上改善了一門語言設計系統的限制;語言各有優缺點用在合適的位置都很好;微軟的.NET/F#的出現頗有多是爲了解決相似問題的,函數式語言是天生的規則模型語言;

【規則引擎的位置】:

在Business Layer 中處處充滿了業務規則,規則引擎是獨立的系統組件,自己的位置應該處於Infrestructure Cross Layer中,可是它屬於架構框架會對業務層有衝擊性,若是設計很差的話甚至會嚴重污染DomainModel,因此挑選一款合適的規則引擎組件或者本身開發都要很慎重;

4.1圖

(注:查看大圖)

上圖中咱們看到對於Student、Teacher、Parent三個角色的幾個用例活動,用例的活動行爲有些是在模型內部的,而有些是在應用層處理的;可是都離不開當前的業務規則,按照上述描述咱們的規則引擎是統一管理業務規則的地方,對於任何一個環節須要業務規則的都將經過在規則引擎中獲取而且直接執行;規則引擎是實時運行着的,對於大型實時在線系統必需要知足這點,不可能將規則保存在磁盤文件上,好比關係型數據庫中、序列化的文件中;應該將它保存在內存中或者經過分佈式緩存技術放在能夠很快且很方便獲取的地方;

這樣將規則獨立出來能夠改變不少事情,甚至有可能顛覆你對業務系統業務邏輯的設計思路;只有這樣設計才能真正談得上是最大粒度的擴展性;咱們進一步擴展,將Business Specification Engine Component 設計於在後臺管理中進行動態規則維護;

4.2圖

(注:查看大圖)

在原圖中咱們加入了SOA接口層,該接口層是通用的後臺維護接口;一般SOA被放在真實的用戶端所調用的外網,可是這裏的位置是出於公司內部網絡,能夠減小關於安全方面的設計,接口都是對於規則的設計入口;規則被維護後會迅速在內存中處於運行狀態,當咱們須要規則的時候直接被規則引擎執行;

目前來講這樣的系統架構對於高擴展性業務系統來講急需,尤爲是在線的個性化定製產品,都會有後臺的客服人員或者是信息人員來根據用戶的須要來設計業務規則;好比某一家企業須要咱們爲他的ERP系統中的定時發貨邏輯(天天10點,貨物的總額必須大於1000元….等等),隨時修改爲但願的參數,這個時候咱們要麼去數據庫中修改,要麼去程序的配置文件修改;

固然好東西是不容易獲得的,這塊尚未成熟的框架支持,若是須要咱們得本身去研究實踐了,這裏只是擴展一下領域;

1.4.2】精簡模型

模型的設計並非最終獲得一張龐大的蜘蛛網,更不能爲了這張蜘蛛網而沾沾自喜,若是你不及時重構的話它很快讓你下不了臺;可是隨着咱們的設計時間推移,需求逐漸的變多模型圖慢慢變大應該是正常的纔對;問題並非說大就是錯誤的,而是大了咱們就很難控制它了,要時刻讓它在你能控制的範圍內;

模型的重構是迭代的過程,若是隻是等到最後再草草的重構一下,僅僅是爲了知足一個理想化的過程而已,那就沒有必要了;重構要實時記在內心,當一羣模型逐漸變的愈來愈大的時候就要及時對它進行精簡,可是前提是不能破壞模型表達的業務知識;

【實體的關聯】

想要讓模型容易控制,固然首要的是砍斷一些沒必要要的關聯,從技術角度考慮一下若是一張龐大的關係網讓程序去實現的話會很是的困難,甚至是不可能穩定實現的;因此說領域驅動設計強調的核心精神是分析、設計必須在一個上下文中,一般這須要一個或者一組人員在必須固定的狀況下完成,這才能保證領域知識有效的吸取和在team內部傳遞;

那麼如何把一張龐大且複雜的模型網合理的切割成精簡的小模型,並且業務模型不會被破壞;一般咱們考慮切割複雜功能的時候都是從功能出發的,包括不少如今系統重構都是這樣的,會出現不少零碎的Function,要說有用吧這些零碎的Fcuntion都須要,要說沒用吧均可以放在一個方法裏,爲何會這樣?由於咱們更本沒有深思問題出在哪裏,或者說更本沒有真正吸取大師們書中的意思;其實問題的重點是咱們根本沒有考慮業務模型,功能的劃分都是理所固然的,小粒度的方法抽取,其實只不過從一個坑裏搬到另外一個坑裏而已,Service層一眼看上去全是幾乎名稱類似的方法,你根本分不清具備什麼樣的業務邏輯的;

重構的正確方向是按照業務邏輯劃分,必須嚴格按照業務流程來走查場景,固然這裏的重構包括對現有系統的重構,無論你的系統是否是DDD驅動的,都是須要根據業務流程來抽取功能點的,切忌重構的粒度不只僅是方法而是邏輯Module;

到目前爲止咱們的全部業務模型都基本上出來了,雖然不是很複雜可是也充分體現出了模型驅動開發的優勢,它很便於咱們對業務的梳理和對須要的把握。其實到目前咱們對系統都沒有進行實質性的編碼或者設計數據庫,在以往這個時候數據庫已經出來了,而後對着一張E-R圖討論系統的需求。可是這裏咱們還在討論需求和分析業務的階段,咱們用UML模型來與業務專家敲定潛在的需求;

4.3圖

這是最終的模型,好似一張蜘蛛網,這樣的模型雖然能直接反饋出真實的業務場景,可是程序設計沒法實現或者說實現起來更本不能用,這就是爲何DDD反覆強調建模人員必定要懂得程序設計、開發,以往就是由於咱們將分析、設計分開來致使領域知識沒法傳遞到設計階段,分析的模型其實根本沒有幫助程序在設計階段提供幫助;

這裏咱們將把這張網變成程序中能夠使用的精簡型的多個小網,並且這些小的網不能破壞模型與現實之間的這種穩定性,其實就是將這複雜的關係能合理、平衡的減小,由於在真實的程序操做當中確定是有業務縫隙的,也就是業務功能都是一個一個處理的,工做的流程也體現出每個流程上而不是一股腦的將全部的流程設計到的模型都拉出來;

【肯定聚合邊界】

肯定聚合邊界是要根據業務來劃分的,那麼如何減小模型的直接的關聯?模型之間的關聯是真實的業務關係,這裏咱們須要將它的直接關係改爲經過ID的方式關聯。在程序中咱們能夠很方便的進行相似Id、Key這種惟一標識來做爲下一步的輸入數據,由於咱們的大部分的數據都在關係型的數據庫中,因此咱們首要考慮的是將模型與模型之間的引用關係改爲Id的關聯;這種方式有一個好處就是延遲的加載,在單個業務處理中不須要把全部的數據都讀取到內存中,而只須要能知足本次業務處理的便可,由於無論什麼系統都有業務流程的前後順序性;

精簡模型的兩個核心過程:實體的關聯、肯定聚合邊界實際上是一個過程的兩個考慮點,只有肯定了聚合邊界才能將聚合內部與外部的關係砍斷。

咱們來調一個現有模型來分析:

4.4圖

目前來講這個點是關聯最多的地方,先來簡單的介紹一下這個模型的大概意思:

【FieldExamination】是表示【每場考試】、【Employee】是表示【員工】、【Subject】是表示【課程類別】枚舉,目前咱們看的見就這三個;

上面曾說過藍色背景的模型是潛在的模型,這裏咱們須要精簡的是【每場考試FieldExamination】模型,上圖中能夠看到以它爲聚合的關聯有四個,咱們就來看【Employee】模型,它是表示全部的學校員工信息,從【FieldExamination】【Employee】有一個Principal聚合,對於每場考試咱們一般都是有一個負責人的,在考試期間可能會去巡查考試紀律包括站考老師是否真的嚴格站考;好像沒有問題啊,就應該有一個複雜人才對啊,可是【Employee】還關聯一些其餘模型:

4.5圖

這樣下去一個連接一個,牽一髮而動全身,根本沒法使用就連重構都很困難;那麼咱們如何尋找要斷開的連接點呢?咱們須要考慮【聚合】的範圍,在上圖中的【FieldExamination】中,咱們若是須要關聯本場考試的負責人是誰那麼在當【FieldExamination】被讀進到內存的時候就被一塊兒關聯出來,可是當咱們考慮真實的業務需求的時候到底需不須要將【Employee】帶出來,【Employy】被帶出來的時候就要牽扯到【Employee】所涉及的關聯;

在咱們的程序UI層中展示出正在進行的【FieldExamination】時,咱們的管理者很但願一眼就能看到本場考試的負責人是誰,而不是在進行一次查詢(無論是異步仍是同步)動做,看來咱們還不能將【Employee】與【FieldExamination】斷開;咱們看到【Employee】關聯着的是兩個基本的枚舉類型,【Employee_Role:員工角色】、【Sex_Type:員工性別】,因此將【Employee】帶出來應該不會有什麼問題;

咱們再回頭來看【FieldExamination】關聯,【Employee】這條線咱們先放下了,看一下【Subject:科目】關聯:

4.6圖

很簡單的一個枚舉類型,關係不大;如今跟【FieldExamination】聚合關係就剩一條了,咱們來看看到底須要不須要關聯;

4.7圖

粗線圈出來的是兩個模型的範圍,內部一個小矩形是從【FieldExamination】到【TestBook:考試試卷】的關聯;每場考試都會有一份本場考試的試卷,可是這裏咱們若是將試卷帶出來的話,那麼試卷模型也會牽扯到它所關聯的模型;真是的業務須要咱們徹底能夠將它斷開了,對於每場考試的輸出咱們不須要知道本場考試的試卷是什麼,只有須要的時候纔會去查詢它,這個時候咱們能夠使用關聯Id來斷開鏈接;

固然真實的環境確定是要比這個複雜不少,要平衡不少的業務環節;

1.4.3】模型擴展性

到目前爲止咱們基本上看見了系統設計的大概雛形了,這也是建模的好處,會讓你對系統的全部業務點有一個比較全面的瞭解和深思;那麼接下來咱們會面臨着系統設計環節中的重點「模型擴展性」那麼什麼叫模型擴展性?簡單點講就是「業務邏輯擴展性」,如何將業務邏輯抽取出來造成必定程度的可配置性;那麼首要的問題是咱們要可以識別出真正會變化的業務點,而不是盲目的把不重要的或者一棍腦的全部的點都抽出去,這樣很不切實際;

其實模型擴展性牽扯到的話題會比較複雜,這也是系統設計中比較難的點,多少年了依然如此,沒有很好的方案可行;目前我總結了可能會對模型擴展性起到啓發性的技術「規則引擎」「凍結程序的延續」「元數據驅動設計」「元編程」「領域特定語言」,對於這些技術咱們須要不少時間去實踐驗證它們到底如何和DDD結合,要否則也是紙上談兵;有幸本人對這些東西有略微的實踐,可是限於篇幅的問題並且這些東西也不是三兩句話就能講清楚的,這裏算是給你們分享一下這些東西,有興趣的朋友能夠去看看,往後一塊兒討論;

那麼這裏要講的是在模型中咱們如何在第一層面上剝離出陷在代碼中的邏輯,將隱式的業務邏輯顯示化,這也是DDD設計過程當中的必需要去完成的,要否則往後碰見業務邏輯修改的時候再來重構的話就很麻煩;

咱們在現有的模型中找一個業務點來分析抽取它,我一直對時間這個東西比較討厭,發現它在任什麼時候候均可能會被修改配置,因此就它了;在咱們【Student學生用例】中就有一個提早交卷的時間限制,可能各個負責人對提早提交時間的態度都不一樣,每次考試的負責人基本上都不同,要否則還不累死;那麼咱們模擬一個簡單的過程:

//當前所剩時間必定要小於或者等於10分鐘

if (this.LeaveTime <= 10)

{ 
    //容許提早交卷而且備註提早交卷的記錄

    ExpeditSubmitLog=new ExpeditSubmitLog();

    ……

}

這是最日常的代碼了,可是通常懂點程序擴展性的都會說這裏的「10」分鐘不能寫死了,恩不錯,確實不能寫死了,哪天要改爲「20」分鐘的就完了;因而咱們將代碼改爲這樣了;

if (this.LeaveTime <= fieldExamination.Advance)

{      //容許提早交卷而且作上提早交卷的記錄

     ExpeditSubmitLog=new ExpeditSubmitLog();

     ……

}

這裏的【fieldExamination】是本場考試對象模型,裏面有一個Advance屬性是用來記錄本場考試能提早多少分鐘交卷的,貌似能經過配置來解決固定時間交卷了,恩 不錯;可是咱們在仔細觀察代碼發現咱們將「提早交卷而且作上提早交卷的記錄」業務邏輯散開在代碼中了,這樣對咱們系統維護性來講頗有威脅得把它抽出來對象化才行,可是問題並無那麼簡單,這裏的邏輯判斷很簡單:「只要知足fieldExamination.Advance屬性小於this.LeaveTime」 就能夠進行下面的操做,你能夠將Advance屬性配置成任何值,只要你的需求是正確的;那麼若是這個時候邏輯判斷再也不是一個簡單的判斷變成了多重判斷,並且不一樣的判斷執行不一樣的邏輯操做,也就是說不一樣的邏輯判斷跟下面的業務操做是一塊兒的;

代碼多是這樣:

if((this.LeaveTime<=fieldExamination.Advance) && fieldExamination.Subject==Subject.English))

{

      //容許提早交卷而且作上提早交卷的記錄

      ExpeditSubmitLog=new ExpeditSubmitLog();

      …… 
}

對於提早交卷的判斷可能有多個條件,每一個條件之間可能有着與或非的關係,這實際上是【DDD中規約模式】解決的問題,將多個條件判斷都設計成可獨立可組合使用的對象模型,經過策略模式將條件對象依次的組合使用確實解決了條件判斷的問題;

ExpeditSubmitSpecification expeditSubmitSpecification=new ExpeditSubmitSpecification();

expeditSubmitSpecification.Add(new ExpeditSubmitLeaveTime(),ExpressionType.And);

expeditSubmitSpecification.Add(new ExpeditSubmitSubject().ExpressionType.Or);

if(expeditSubmitSpecification.CheckChaining())//進行提早交卷的邏輯判斷;

{

      //容許提早交卷而且作上提早交卷的記錄

      ExpeditSubmitLog=new ExpeditSubmitLog();

      …… 
}

這裏咱們已經能夠將條件判斷的邏輯對象化了,可是對於提早交卷的業務邏輯仍是散開在方法體中的,還記得咱們上面曾講過「規則引擎」嗎,將規則的配置在外部設置,我想對象化後這個已經不是不什麼難事了,可是對於到底怎麼提交【提早試卷】的動做可能不僅是一種方式:

if(expeditSubmitSpecification.CheckChaining())//進行提早交卷的邏輯判斷;

{

    //容許提早交卷而且作上提早交卷的記錄 
    IExpeditSubmit iexpedit=IoCComponent.Resolve<IExpeditSubmit>();//經過IoC的方式獲取,這裏其實已經將業務邏輯配置化了;

    iexpedit.Submit(ExpeditSubmitContext.CurrentContextInfo);

}

判斷跟執行邏輯是兩個不太相干的過程,不一樣的判斷能夠執行一組邏輯或者不一樣的判斷執行不一樣的邏輯,這是根據咱們配置來的;

固然這裏只是擴展性的介紹一下,本主題在後面的文章中會專門去介紹和研究;

1.5】系統架構設計、DDD分層架構

需求驅動了架構,對於DDD的架構跟以往的架構有着很大的不一樣,爲何不一樣與傳統的架構由於關注的東西從一開始就是不一樣的;爲了將前期分析出來的DDD模型在系統中體現出來,也就是將DDD理論分析完全落實在代碼上這須要將業務模型做爲重點關注對象,因此架構的焦點從原來的組件型、框架型變成了只關注領域模型的架構;

在咱們腦子裏傳統的系統架構都是簡單的分層架構固然我是指目前絕大部分的企業中,也就是傳統的三層架構或者說四層架構,固然用的如何就各式各樣了;架構自己沒有好壞之分只有合適不合適之選;對於咱們之前傳統的三層架構,簡單明瞭很好理解,對開發者的要求門檻很低基本上看一遍就知道怎麼寫了;話說回來無論是什麼類型的系統都須要將其進行簡單的分層來分離關注點,在這點上你們都沒有問題,各自取長補短,這是計算機科學多少年驗證的事實,是用來解決複雜問題的通用解決方案;

上面也說了分層架構在每一個公司裏面的實現都是不一樣的,都或多或少的有點特性化在裏面,這也是正確的設計,沒有一勞永逸的架構,可是世界在變全部東西都在變本來咱們只關注技術實現的自己忽視了咱們本應該重視的業務實現,可是這些技術如今已經很成熟了咱們是時候會過頭來關注一下軟件設計的本質了;

從一開始咱們就被一個很大的謊話所欺騙着,在咱們還不是太懂這個」社會「的時候被「某些「自認爲是」專家「的老師將咱們引入了一個生存之道的反方向;咱們與真正的軟件設計背道而馳,當咱們慢慢恢復一我的所應該有的洞察力時咱們發現其實世界不是這樣子的,咱們是能夠活的很好的,咱們徹底有能力來對付一個龐大的系統,之因此咱們之前無能爲力是由於咱們從一開始就錯了!

架構被多方面緣由驅動着, 從技術方面講:硬件、大數據、高併發等等,業務方面:低延遲性、高時效性等等,那麼架構真的是咱們所理解的那樣嗎?固然我沒有這個智慧去總結它究竟是什麼樣子的,當只有同樣東西的時候咱們很難說出它的好與壞,只有當咱們將兩個東西在一塊兒比較的時候才能根據互相的參照物進行好與壞的平衡;在計算機領域沒有絕對的好與壞,由於這個世界就沒有絕對的事情;

隨着如今的大數據的到來,咱們的架構是否能應付這麼龐大的數據流,是否能抗的住另外一方面的衝擊:高併發等等諸如此類的問題,用簡單的一門技術是很難解決一個龐大的問題鏈的,咱們須要結衆家之所長來一塊兒對付這些所謂的IT發展的問題流,他們一波又一波的衝擊着咱們;

那麼對於DDD的架構它將與傳統架構有着哪些區別,它將爲咱們帶來哪些的技術和思想,你也許會問爲何DDD不同凡響,我很高興的告訴你:「由於它面向領域驅動「;用我親身體會來總結一句對DDD的認識:」DDD是系統分析、設計、架構的最佳實踐驗證「,因此我更喜歡稱咱們程序員最佳驗證者也是最佳實踐者;

1.5.1】傳統分層架構

在咱們每一個程序員的腦子裏都有一個本身的架構模型,每一個人的技術底蘊不一樣架構有強大和簡單的,可是都是在分層架構上延伸出來的;咱們先來回顧一下傳統的架構,這裏要解釋一下一個概念就是邏輯架構與物理架構的區別,咱們所討論的是邏輯架構也就是代碼solution中的結構,從這裏面會映射到物理架構中,好比咱們的分層架構中都會有cache的功能,在全部的層面上都要進行緩存的功能調用,那麼確定是須要將cache的功能進行公共的封裝而後調用,可是這個cache的最終是須要部署到服務器上的,這裏就造成物理架構的映射;

5.1圖

面對這張圖咱們在熟悉不過了,基礎框架Common Component裏的全部功能都是在全部層面上共用的,邊界必定要清晰;在Application Layer中有一個很簡單的服務接口是專門用來對外開發功能的,這裏不是SOA只是面向客戶端的WEB接口或者是面向socket的接口都行;

這樣的架構一如既往的被咱們使用着,簡單明瞭,可是咱們發現它彷佛到頭了,問題接踵而來;業務邏輯鋪滿UI層,隨着時間的推移咱們的代碼變的難以維護,系統面臨着進退兩難的地步,這不是技術人員的問題也不是設計的問題只能說是技術在進步;

因此面向領域驅動分層架構能夠解決上面提到的問題,固然也會帶來新的問題,不過不要緊世界萬事萬物都是有兩面性的,有對就有錯,有好就有壞,問題始終是要被解決的,關鍵是咱們能有勇氣去解決它;

1.5.2】DDD充血型架構

對於DDD的架構會讓咱們很新奇,至少我第一次看見它的時候很激動一會兒打開了「軟件工程」的大門,種種問題一下有思路了;固然前提是你曾經埋怨過架構的設計不足帶來的問題,若是系統沒法重構那麼它已經80%死掉了,隨着你維護的速度加快它會死的更快;(必定有人有相同的感覺)

咱們來了解一下DDD的分層架構,我相信你確定會有不少疑問的,不要緊接受任何新的事物都須要一個過程,關鍵是有充分的理由才行;

5.2】圖

一種方法論誕生以後首要的是要用一種可行的理由說服大衆,那樣才能既衆人之力來完善它;DDD固然也是如此,從五年前的DDD推出以後一隻到如今已經發展的很成熟且很強大;上圖中咱們能夠很明顯的看出咱們弱化了除領域層以外的層,架構來自需求的驅動;

DDD強調咱們始終聚焦於軟件的核心,永遠都不要離開業務模型,將全部的規則都封閉在DomainModel中堅定不能讓業務規則泄漏出去;這個時候咱們不會關心你是用什麼UI框架,用什麼數據持久化框架,這些都是次要的;價值觀必定不要離開領域模型;

DomainModel不會對外部進行任何調用,持久化將在Application Layer中處理,保證DomainModel是一個POJO的對象;在圖的右邊是infrastructure,能夠把它理解成基礎設施,可能你會有點想不通,從功能上看不是和上圖中的Common Component同樣嗎?恩 確實差很少,從技術角度講任何東西是差很少的,可是咱們考慮的是模型化設計,面向對象設計;問題不在是簡簡單單的技術問題了而是設計思想的層次了,若是不提高設計思想那計算機也不會發展成今天這樣;從抽象的角度講,一切都圍繞着DomainModel,爲了支撐DomainModel的運行的都屬於基礎性設施;「設施」一詞很富有高層設計意義,咱們已經不在用功能、函數來支撐系統運行了;固然DomainModel的美還須要你親自去接觸才能體會到,這裏只是一個介紹;

隨着DomainModel的不斷龐大,對系統的性能有必定的影響,因此上面曾說過凡事都有兩面性,這樣充血型的架構會將模型變的很肥胖,因此咱們必需要找到解決辦法才行,萬幸的是咱們站在巨人的肩膀上的;對於這樣的問題DDD.CQRS架構誕生了,爲了解決充血性的DDD架構;

(限於篇幅關係再加上這篇文章不是專門講架構的,因此這裏有興趣的朋友能夠本身去查詢相關的資料DDD.CQRS架構 、DDD.EDA架構 ;)

1.6】數據存儲設計

一直到如今咱們都是在設計、分析對象關係,可是事實是咱們的對象都運行在內存中才是這一切的本質;對象只有被載入到內存中才能讓他們活動起來,可是對象始終是要被持久化保存起來的,也就是離不開數據存儲技術;目前咱們廣泛使用關係型數據庫來存儲數據,固然也能夠放在任何NoSql數據庫中;固然最優的方案是In-memory,將DomainModel直接緩存在內存中,可是這項技術目前不是很成熟或者說對他掌握的人不多;按照DDD的架構設計咱們將不直接依賴於數據存儲框架,不會受限於數據持久化的約束;固然咱們徹底能夠將DDD存儲在XMLDOM中,讓後將XMLDOM緩存在Memory中,原本DOM就有很強的表現能力,從XAML就能看出DOM在後面將會在不少地方用到;

大膽的構想XML將被用做於領域特定語言上,對特性領域的抽象將不在侷限於某種編程語言;語言是用來交流,本來的編程語言是程序員用來跟計算機交流的語言,可是技術在發展,編程語言將進一步被抽象被掩蓋在底層,好久之後咱們將不在須要直接編寫程序語言,將經過與領域專家的合做開發出符合特定領域的語言,將用這種專門的語言來生產軟件;

做爲Microsoft.NET平臺的咱們,若是對領域特定語言有興趣的朋友能夠推薦看一下這本書《VisualStudent DSL 工具特定領域開發指南》,內容比較深,對分析設計架構建模 均要有必定程度的熟悉,不過能夠做爲技術研究嘗試一下;

數據存儲設計須要結合實際的需求和架構來的,大型電子商務和企業基本的ERP在數據存儲設計上確定是有所偏重的,好比電子商務在架構設計上可能容許低延遲性、數據最終一致性,而ERP可能須要響應及時性,數據實時一致性,這自己就須要平衡的;

分佈式領域的CAP定理 你們應該都有所聽聞,分佈式系統必需要平衡好三要素:Consistency(一致性), 數據一致更新,全部數據變更都是同步的;Availability(可用性), 好的響應性能;Partition tolerance(分區容錯性) 可靠性;

定理:任何分佈式系統只可同時知足二點,無法三者兼顧。 忠告:架構師不要將精力浪費在如何設計能知足三者的完美分佈式系統,而是應該進行取捨。

——引自(解道.板橋里人)

其實這裏的數據存儲設計已經不是一個建立Table的那麼簡單了,如今動不動就大數據量,高併發因此咱們如何將DomainModel放入存儲設施;這確實很難,設計的很差將對後面的系統總體架構帶來難以擴展影響,固然這也不是本篇文章討論的問題我也不具有這樣的能力;這裏要討論的是如何映射DomainModel到關係型數據庫;關係數據庫是面向關係模型,而咱們的DomainModel是複雜的面向對象模型,如何在這二者之間很平滑的映射,咱們須要ORM框架的支持;沒有很好的ORM框架很難解決一些純技術問題,這裏咱們固然是使用.NETEntityFramework框架來支撐(後面本博客將有一個系列詳細的深刻解析EntityFramework框架的文章),固然也能夠使用其餘的ORM框架,開源的也好、免費的也好;每種框架的映射原理不一樣,這裏就不一一講解了,使用EntityFrmework映射其實很簡單的,網上也有不少使用文章;

1.6.1】模型與關係數據之間的平衡

在文章中處處充滿着模型一詞,模型是具體事物的抽象表現,他是人最直觀的接收方式;那麼畢竟模型是虛擬的東西,只是一種幫助理解的描述而已,將模型等價的持久化到任何數據存儲容器中都須要能平衡的進行模型與數據結構之間的映射才行,無論你是SQL也好仍是NOSQL也好,都須要事先構造好這種映射關係才行;模型的設計理論是面向對象,而大部分的數據源存儲容器基本上都是SQL數據庫,如何很好的將模型在數據庫中持久化這在架構上也須要必定的要求和調整;

【繼承深度要控制好】

因爲繼承很難在關係型模型中體現,數據庫須要很牽強的表達這種關係(你能夠使用EntityFramework的Model  First試一下);因此繼承層次多了很難在Repository中解決,固然也不是說不用繼承,只是說層次不要多,不能像設計框架那樣隨意的設計類,Domain  Model 儘量的簡單;

【儘可能避開Reposiroty在User\Role中】

在咱們使用DCI架構而且使用行爲驅動設計來捕獲系統的需求的時候,傳統架構中的對象將在系統中不復存在了,系統中充滿了場景角色數據,讓面向對象上了一層樓,更讓面向DDD的分層架構上了一層高樓;若是將不少與Repository相關的行爲放入角色、用戶對象中將帶來不少耦合,固然只有去作一次才能真正體會到,這裏我只是總結一下;

總結:本文很長,花了我不少時間,不過很值;但願本篇文章能幫你們簡單的掃盲一下關於DDD的相關技術,文章沒有太多高深的技術,只是一些咱們不太接觸的理論技術而已,其實咱們真的有必要尋找一條正確的軟件工程道路,面向對象分析設計若是脫離編碼那一點意義都沒有了,至少目前確實是這樣的;

那麼又有多少人真正知道問題在哪裏呢?很好笑,我也不知道,可是我能夠很明確的告訴你們,DDD是尋找解決問題的思路,也是通往光明的正確道路,但願對DDD有興趣的朋友能夠去專研它,就算當成興趣愛好也行,千萬別視而不見由於將在下一次的技術革命中DDD會大面積爆發;謝謝;

 

相關文章
相關標籤/搜索