[譯文]Domain Driven Design Reference(三)—— 模型驅動設計的構建模塊

本書是Eric Evans對他本身寫的《領域驅動設計-軟件核心複雜性應對之道》的一本字典式的參考書,可用於快速查找《領域驅動設計》中的諸多概念及其簡明解釋。html

其它本系列其它文章地址:數據庫

[譯文]Domain Driven Design Reference(一)—— 前言服務器

[譯文]Domain Driven Design Reference(二)—— 讓模型起做用網絡

[譯文]Domain Driven Design Reference(三)—— 模型驅動設計的構建模塊架構

Ⅱ.模型驅動設計的構建模塊

  這些模式根據領域驅動設計,普遍地推行了面向對象設計的最佳實踐。他們指導決策來提煉模型,並使模型和實現保持一致,每個都加強了其餘的有效性。仔細制定模型元素的細節爲開發人員提供了一個穩定的平臺,從中能夠探索模型並使其與實現保持緊密聯繫。框架

分層架構

  在面向對象的程序中,用戶界面,數據庫和其餘支持代碼一般會直接寫入業務對象。額外的業務邏輯被嵌入在UI部件和數據庫腳本的行爲中。發生這種狀況是由於在短時間內,這樣作是最簡單的方法。異步

  當與領域相關的代碼經過如此大量的其餘代碼被擴散時,變得很是難以理解和推理。UI的表面變化實際上能夠改變業務邏輯。要更改業務規則,可能須要仔細跟蹤UI代碼,數據庫代碼或其餘程序元素。實現一致的、模型驅動的對象變得不切實際。自動化測試變得難以進行。因爲每一個活動都涉及到全部的技術和邏輯,程序必須保持很是簡單,不然就沒法理解。分佈式

  所以:函數

  隔離領域模型和業務邏輯的表達形式,並消除對基礎架構,用戶界面甚至非業務邏輯的應用程序邏輯的依賴。將一個複雜的程序分紅多個層。在每一個層次內開發一個內聚的設計,而且僅依賴於下面的層。遵循標準的建築模式,爲上面的分層提供鬆散的耦合。將全部與領域模型相關的代碼集中在一個層中,並將其與用戶界面,應用程序和基礎設施的代碼隔離。領域對象沒有顯示本身,存儲本身,管理應用程序任務等等的職責,能夠集中在表達領域模型上。這使得一個模型可以發展到足夠豐富,足夠清晰,可以捕獲必要的業務知識並將其付諸實踐。工具

  這裏的關鍵目標是隔離。 諸如「六邊形架構」之類的相關模式能夠起到容許咱們的領域模型表現避免依賴和引用其餘系統問題,甚至更好的效果。

實體

  許多對象表明了一個連續的具備身份標識的主線,貫穿整個生命週期,儘管其屬性可能會改變。一些對象不是主要由它們的屬性定義的。它們表明了貫穿時間並常常跨越不一樣展示形式的主線的身份標識。有時這樣的對象必須與另外一個對象匹配,即便屬性不一樣。錯誤的身份可能致使數據損壞。

  所以:

  當一個對象被它的身份而不是它的屬性所區分時,把它做爲它在模型中定義的要點。保持簡單的類定義,並關注生命週期的連續性和身份標識。

  定義一個區分每一個對象的方法,而無論它的形式或歷史。 對經過屬性調用匹配對象的需求保持警戒。定義一個保證爲每一個對象產生惟一結果的操做,可能經過附加一個保證惟一的符號。這種標識手段可能來自外部,也多是由系統建立的任意標識符,但必須符合模型中的身份標識區別。

  模型必須定義什麼是一樣的事情。

  (又稱參考對象)

值對象

  有些對象描述或計算事物的一些特徵。

  許多對象沒有概念上的身份標識。

  跟蹤實體的身份標識相當重要。但將身份標識附加到其餘對象可能會傷害系統性能,增長分析工做,並使全部對象看起來都如出一轍。軟件設計是一個複雜的持續戰鬥。咱們必須做出區分,以便只有在必要時才進行特殊處理。

  然而,若是咱們把這種類型的對象看做是缺乏身份的話,那麼咱們並無在咱們的工具箱或詞彙中添加太多東西。實際上,這些對象具備本身的特色,對模型自己也有意義。 這些是描述事物的對象。

  所以:

  當您只關心模型元素的屬性和邏輯時,將其歸類爲值對象。使其表達它傳達的屬性的含義並賦予它相關的功能。將值對象視爲不可變的。使全部操做是不依賴任何可變狀態的無反作用函數。不要給值對象任何身份標識,並避免保留實體所必需的設計複雜性。

領域事件

  領域專家關心的事情發生了。一個實體負責跟蹤其狀態和規定其生命週期的規則。可是,若是你須要知道狀態變化的實際緣由,這一般是不明確的,而且可能很難解釋系統如何實現它。審計線索能夠容許跟蹤,但一般不適合用於程序自己的邏輯。實體的變化歷史能夠容許訪問先前的狀態,但忽略這些變化的含義,以便對信息的任何操做都是程序性的,而且常常被拋出領域層。

  分佈式系統中出現了一系列獨特但又相關的問題。分佈式系統的狀態在任什麼時候候都不能保持徹底一致。咱們始終保持聚合內部一致,而異步的進行其餘更改。當更改在網絡的節點間傳播時,可能很難解決無序或來自不一樣來源的多個更新。

  所以:

  將關於領域中活動的模型信息視爲一系列離散事件。將每一個事件表示爲一個領域對象。這些不一樣於系統事件,它們反映了軟件自己的活動,雖然一般系統事件與領域事件相關聯或者做爲領域事件的響應的一部分,或者做爲將領域事件的信息攜帶到系統中的一種方式。

  領域事件是領域模型的一個完整的部分,是領域中發生的事情的表示形式。忽略不相關的領域活動,同時明確領域專家想要跟蹤或者被通知的事件,或者與其餘模型對象中的狀態改變相關聯的事件。

  在分佈式系統中,實體的狀態能夠從特定節點的當前已知的領域事件中推斷出來,從而在沒有關於整個系統的完整信息的狀況下獲得相關的模型。

  領域事件一般是不可變的,由於它們是過去的某種事物的記錄。除了對事件的描述以外,領域事件一般包含事件發生時間的時間戳以及事件涉及的實體的身份標識。此外,領域事件一般具備單獨的時間戳,指示事件什麼時候進入系統以及使其進入系統的人的身份標識。若是有用,領域事件的身份標識能夠基於這些屬性的一些集合。因此,例如,若是同一個事件的兩個實例到達一個節點,則它們能夠被識別爲相同的。

服務

  有時候,這不是一回事。領域的一些概念由模型做爲對象是不天然的。強制所需的領域功能成爲實體或者值對象的職責,要麼篡改基於模型的對象的定義,要麼添加無心義的虛擬對象。

  所以:

  當領域中的重要流程或轉換不是實體或值對象的天然職責時,添加一個操做到模型中做爲一個單獨的接口同時聲明爲一個服務。定義一個服務契約,一組關於與服務交互的聲明。用一個特定限界上下文的通用語言來陳述這些聲明。給服務一個名字,這也成爲通用語言的一部分。

模塊

  每一個人都使用模塊,但不多將它們視爲模型的完整部分。代碼被分解成各類類別,從技術架構的各個方面到開發人員的工做任務。即便是作了不少重構的開發人員也傾向於使用項目早期構思的模塊。

  耦合和凝聚力的解釋傾向於使它們聽起來像是技術指標,根據關聯和相互做用的分佈進行機械的判斷。然而,這不只僅是將代碼劃分爲模塊,還包括概念。一我的一次能夠思考多少事情是有限的(所以耦合度低),不連貫的想法片斷很難被理解爲一個無差異的想法(所以具備很高的內聚性)。

  所以:

  選擇可以講述系統故事的模塊,幷包含一系列內聚的概念。讓模塊名稱成爲通用語言的一部分。模塊是模型的一部分,它們的名稱應反映對領域的洞察。

  這一般會致使模塊之間的低耦合,可是若是它不尋找一種方法來改變模型來分解概念,或者是一個被忽視的概念,它多是一個可以以有意義的方式將元素組合在一塊兒的模塊的基礎。在能夠被獨立地理解和推理的概念上尋求低耦合。根據高層領域概念對模型進行細化直到它被劃分,並將相應的代碼解耦。

聚合

  要保證複雜關聯模型中對象變化的一致性是很困難的。他們可以被是概念上的構成部分的其它對象的變化所掩蓋。在多個服務器之間分發對象或設計異步事務時會出現相似的問題。

  所以:

  將實體和值對象集中到聚合中並在周圍定義邊界。選擇一個實體做爲每一個聚合的根,並容許外部對象僅保留對根的引用(對內部成員的引用僅在一個操做中返回出去才能使用)。定義聚合的屬性和不變量做爲一個總體,並將這個約束的責任賦予根【這裏指的是聚合根】或某種指定的框架機制。

  使用相同的聚合邊界來管理事務和分配。

  在一個聚合邊界內,同步地應用一致性規則。 跨越邊界,異步地處理更新。

  在一臺服務器上共同維護一個聚合。容許不一樣的聚合在節點間分配。

  若是這些設計決策沒有受到聚合邊界的良好指導,請從新考慮模型。是領域的場景正在暗示着一個重要的新看法嗎?這種改變一般會提升模型的表現力和靈活性,並解決事務和分配問題。

倉儲

  查詢通用語言表達的聚合。

  可遍歷的關聯的擴散只用於找到弄亂模型的東西。在成熟模型中,查詢常常表達領域概念。然而查詢可能會致使問題。

  應用大多數數據庫訪問基礎架構的純粹技術複雜性迅速吞噬了客戶端代碼,致使開發人員陷入了領域層,使得模型可有可無。

  查詢框架可能會封裝大部分的技術複雜性,使開發人員可以以更自動化或聲明的方式從數據庫中提取所需的確切數據,但這隻能解決一部分問題。

  不受約束的查詢可能會從對象中拉出特定的字段,違反封裝,或從聚合內部實例化幾個特定的對象,讓聚合根變得充滿變數並使這些對象沒法執行領域模型的規則。領域邏輯移入查詢和應用程序層代碼,實體和值對象變成僅僅爲數據容器。

  所以:

  對於須要全局訪問的每種聚合類型,建立一個服務,它能夠提供全部聚合根類型的對象的在一個內存集合中的錯覺。經過一個你們都知道的全局接口設置訪問。提供添加和刪除對象的方法,這將封裝實際數據往數據存儲中的插入或刪除。提供基於對領域專家有意義的標準來選擇對象的方法。返回徹底實例化的對象或屬性值符合條件的對象集合,從而封裝實際的存儲和查詢技術,或者返回給予以惰性的方式徹底實例化的聚合的幻覺的代理。僅爲實際須要直接訪問的聚合根提供倉儲。保持應用程序邏輯專一於模型,委託全部的對象存儲和訪問給倉儲。

工廠

  當建立一個完整的,內部一致的聚合或者一個大值對象變得複雜或者顯示太多的內部結構時,工廠提供封裝。一個對象的建立自己能夠是一個主要的操做,可是複雜的組裝操做不適合由建立的對象來承擔。將這些職責結合起來可能會產生難以理解而且難看的設計。讓客戶端直接組裝會混亂客戶端的設計,破壞組裝對象或集合的封裝,而且過分地將客戶端耦合到所建立對象的實現中【舉個例子,這裏的客戶端能夠理解成應用層或者UI層】。

  所以:

  將建立複雜對象和聚合實例的責任轉移到單獨的對象上,這個對象自己可能在域模型中沒有職責,但仍然是領域設計的一部分。提供一個封裝全部複雜程序集的接口,而且不要求客戶端引用實例化對象的具體類。將建立一個完整的聚合做爲一部分,強制實施它的不變性。建立一個複雜的值對象,多是在將元素與構建器組合後。

做者:Zachary_Fan
出處:www.cnblogs.com/Zachary-Fan…

若是你想及時獲得我的自寫文章的消息推送,歡迎掃描下面的二維碼~。

相關文章
相關標籤/搜索