目錄
- 領域驅動設計-領域物件的生命周期
- AGGREGATE
- FACTORY
領域驅動設計-領域物件的生命周期
每個物件都有生命周期,如圖6-1所示,物件自創建后,可能會經歷各種不同的狀態,直至最終消亡——要么存檔,要么洗掉,當然,很多物件是簡單的臨時物件,僅通過呼叫建構式來創建,用來做一些計算,而后由垃圾收集器回收,這類物件沒必要搞得那么復雜,但有些物件具有更長的生命周期,其中一部分時間不是在活動記憶體中度過的,它們與其他物件具有復雜的相互依賴性,它們會經歷一些狀態變化,在變化時要遵守一些固定規則,管理這些物件時面臨諸多挑戰,稍有不慎就會偏離MODEL-DRIVEN DESIGN的軌道,

主要的挑戰有以下兩類,
(1) 在整個生命周期中維護完整性,
(2) 防止模型陷入管理生命周期復雜性造成的困境當中,
AGGREGATE(聚合),它通過定義清晰的所屬關系和邊界,并避免混亂、錯綜復雜的物件關系網來實作模型的內聚,聚合模式對于維護生命周期各個階段的完整性具有至關重要的作用,我們將注意力轉移到生命周期的開始階段,使用FACTORY(工廠)來創建和重建復雜物件和AGGREGATE(聚合),從而封裝它們的內部結構,最后,在生命周期的中間和末尾使用REPOSITORY(存盤庫)來提供查找和檢索持久化物件并封裝龐大基礎設施的手段,
盡管REPOSITORY和FACTORY本身并不是來源于領域,但它們在領域設計中扮演著重要的角色,這些結構提供了易于掌握的模型物件處理方式,使MODEL-DRIVEN DESIGN更完備,
使用AGGREGATE進行建模,并且在設計中結合使用FACTORY和REPOSITORY,這樣我們就能夠在模型物件的整個生命周期中,以有意義的單元、系統地操縱它們,AGGREGATE可以劃分出一個范圍,這個范圍內的模型元素在生命周期各個階段都應該維護其固定規則,FACTORY和REPOSITORY在AGGREGATE基礎上進行操作,將特定生命周期轉換的復雜性封裝起來,
AGGREGATE
減少設計中的關聯有助于簡化物件之間的遍歷,并在某種程度上限制關系的急劇增多,但大多數業務領域中的物件都具有十分復雜的聯系,以至于最侄訓形成很長、很深的物件參考路徑,我們不得不在這個路徑上追蹤物件,在某種程度上,這種混亂狀態反映了現實世界,因為現實世界中就很少有清晰的邊界,但這卻是軟體設計中的一個重要問題,
假設我們從資料庫中洗掉一個Person物件,這個人的姓名、出生日期和作業描述要一起被洗掉,但要如何處理地址呢?可能還有其他人住在同一地址,如果洗掉了地址,那些Person物件將會參考一個被洗掉的物件,如果保留地址,那么垃圾地址在資料庫中會累積起來,雖然自動垃圾收集機制可以清除垃圾地址,但這也只是一種技術上的修復;就算資料庫系統存在這種處理機制,一個基本的建模問題依然被忽略了,
即便是在考慮孤立的事務時,典型物件模型中的關系網也使我們難以斷定一個修改會產生哪些潛在的影響,僅僅因為存在依賴就更新系統中的每個物件,這樣做是不現實的,
在多個客戶對相同物件進行并發訪問的系統中,這個問題更加突出,當很多用戶對系統中的物件進行查詢和更新時,必須防止他們同時修改互相依賴的物件,范圍錯誤將導致嚴重的后果,
在具有復雜關聯的模型中,要想保證物件更改的一致性是很困難的,不僅互不關聯的物件需要遵守一些固定規則,而且緊密關聯的各組物件也要遵守一些固定規則,然而,過于謹慎的鎖定機制又會導致多個用戶之間毫無意義地互相干擾,從而使系統不可用,
換句話說,我們如何知道一個由其他物件組成的物件從哪里開始,又到何處結束呢?在任何具有持久化資料存盤的系統中,對資料進行修改的事務必須要有范圍,而且要有保持資料一致性的方式(也就是說,保持資料遵守固定規則),資料庫支持各種鎖機制,而且可以撰寫一些測驗來驗證,但這些特殊的解決方案分散了人們對模型的注意力,很快人們就會回到“走一步,看一步”的老路上來,
實際上,要想找到一種兼顧各種問題的解決方案,要求對領域有深刻的理解,例如,要了解特定類實體之間的更改頻率這樣的深層次因素,我們需要找到一個使物件間沖突較少而固定規則聯系更緊密的模型,
盡管從表面上看這個問題是資料庫事務方面的一個技術難題,但它的根源卻在模型,歸根結底是由于模型中缺乏明確定義的邊界,從模型得到的解決方案將使模型更易于理解,并且使設計更易于溝通,當模型被修改時,它將引導我們對實作做出修改,
首先,我們需要用一個抽象來封裝模型中的參考,AGGREGATE就是一組相關物件的集合,我們把它作為資料修改的單元,每個AGGREGATE都有一個根(root)和一個邊界(boundary),邊界定義了AGGREGATE的內部都有什么,根則是AGGREGATE所包含的一個特定ENTITY,對AGGREGATE而言,外部物件只可以參考根,而邊界內部的物件之間則可以互相參考,除根以外的其他ENTITY都有本地標識,但這些標識只在AGGREGATE內部才需要加以區別,因為外部物件除了根ENTITY之外看不到其他物件,
汽車修配廠的軟體可能會使用汽車模型,如圖6-2所示,汽車是一個具有全域標識的ENTITY:我們需要將這部汽車與世界上所有其他汽車區分開(即使是一些非常相似的汽車),我們可以使用車輛識別號來進行區分,車輛識別號是為每輛新汽車分配的唯一識別符號,我們可能想通過4個輪子的位臵跟蹤輪胎的轉動歷史,我們可能想知道每個輪胎的里程數和磨損度,要想知道哪個輪胎在哪兒,必須將輪胎標識為ENTITY,當脫離這輛車的背景關系后,我們很可能就不再關心這些輪胎的標識了,如果更換了輪胎并將舊輪胎送到回收廠,那么軟體將不再需要跟蹤它們,它們會成為一堆廢舊輪胎中的一部分,沒有人會關心它們的轉動歷史,更重要的是,即使輪胎被安在汽車上,也不會有人通過系統查詢特定的輪胎,然后看看這個輪胎在哪輛汽車上,人們只會在資料庫中查找汽車,然后臨時查看一下這部汽車的輪胎情況,因此,汽車是AGGREGATE的根ENTITY,而輪胎處于這個AGGREGATE的邊界之內,另一方面,發動機組上面都刻有序列號,而且有時是獨立于汽車被跟蹤的,在一些應用程式中,發動機可以是自己的AGGREGATE的根,

固定規則(invariant)是指在資料變化時必須保持的一致性規則,其涉及AGGREGATE成員之間的內部關系,而任何跨越AGGREGATE的規則將不要求每時每刻都保持最新狀態,通過事件處理、批處理或其他更新機制,這些依賴會在一定的時間內得以解決,但在每個事務完成時,AGGREGATE內部所應用的固定規則必須得到滿足,如圖6-3所示,
現在,為了實作這個概念上的AGGREGATE,需要對所有事務應用一組規則,
根ENTITY具有全域標識,它最終負責檢查固定規則,
根ENTITY具有全域標識,邊界內的ENTITY具有本地標識,這些標識只在AGGREGATE內部才是唯一的,
AGGREGATE外部的物件不能參考除根ENTITY之外的任何內部物件,根ENTITY可以把對內部ENTITY的參考傳遞給它們,但這些物件只能臨時使用這些參考,而不能保持參考,根可以把一個VALUE OBJECT的副本傳遞給另一個物件,而不必關心它發生什么變化,因為它只是一個VALUE,不再與AGGREGATE有任何關聯,
作為上一條規則的推論,只有AGGREGATE的根才能直接通過資料庫查詢獲取,所有其他物件必須通過遍歷關聯來發現,
AGGREGATE內部的物件可以保持對其他AGGREGATE根的參考,

洗掉操作必須一次洗掉AGGREGATE邊界之內的所有物件,(利用垃圾收集機制,這很容易做到,由于除根以外的其他物件都沒有外部參考,因此洗掉了根以后,其他物件均會被回收,)
當提交對AGGREGATE邊界內部的任何物件的修改時,整個AGGREGATE的所有固定規則都必須被滿足,
我們應該將ENTITY和VALUE OBJECT分門別類地聚集到AGGREGATE中,并定義每個AGGREGATE的邊界,在每個AGGREGATE中,選擇一個ENTITY作為根,并通過根來控制對邊界內其他物件的所有訪問,只允許外部物件保持對根的參考,對內部成員的臨時參考可以被傳遞出去,但僅在一次操作中有效,由于根控制訪問,因此不能繞過它來修改內部物件,這種設計有利于確保AGGREGATE中的物件滿足所有固定規則,也可以確保在任何狀態變化時AGGREGATE作為一個整體滿足固定規則,
有一個能夠宣告AGGREGATE的技術框架是很有幫助的,這樣就可以自動實施鎖機制和其他一些功能,如果沒有這樣的技術框架,團隊就必須靠自我約束來使用事先商定的AGGREGATE,并按照這些AGGREGATE來撰寫代碼,
示例 采購訂單的完整性

一個典型的采購訂單(Purchase Order,PO)視圖,它被分解為采購項(Line Item),一條固定規則是采購項的總量不能超過PO總額的限制,當前實作存在以下3個互相關聯的問題,
(1) 固定規則的實施,當添加新采購項時,PO檢查總額,如果新增的采購項使總額超出限制,則將PO標記為無效,正如我們將要看到的那樣,這種保護機制并不充分,
(2) 變更管理,當PO被洗掉或存檔時,各個采購項也將被一塊處理,但模型并沒有給出關系應該在何處停止,在不同時間更改部件(Part)價格所產生的影響也不明確,
(3) 資料庫共享,資料庫會出現由于多個用戶競爭使用而帶來的問題,
多個用戶將并發地輸入和更新各個PO,因此必須防止他們互相干擾,讓我們從一個非常簡單的策略開始,當一個用戶開始編輯任何一個物件時,鎖定該物件,直到用戶提交事務,這樣,當George編輯采購項001時,Amanda就無法訪問該項,Amanda可以編輯其他PO上的任何采購項(包括George正在編輯的PO上的其他采購項)

每個用戶都將從資料庫讀取物件,并在自己的記憶體空間中實體化物件,而后在那里查看和編輯物件,只有當開始編輯時,才會請求進行資料庫鎖定,因此,George和Amanda可以同時作業,只要他們不同時編輯相同的采購項即可,一切正常,直到George和Amanda開始編輯同一個PO上的不同采購項.

從這兩個用戶和他們各自軟體的角度來看,他們的操作都沒有問題,因為他們忽略了事務期間資料庫其他部分所發生的變化,而且每個用戶都沒有修改被對方鎖定的采購項,當這兩個用戶保存了修改之后,資料庫中就存盤了一個違反領域模型固定規則的PO,一條重要的業務規則被破壞了,但并沒有人知道

顯然,鎖定單個行并不是一種充分的保護機制,如果一次鎖定一個PO,可以防止這樣的問題發生

直到Amanda解決這個問題之前,程式將不允許保存這個事務,Amanda可以通過提高限額或減少一把吉他來解決此問題,這種機制防止了問題,如果大部分作業分布在多個PO上,那么這可能是個不錯的解決方案,但如果是很多人同時對一個大PO的不同項進行操作時,這種鎖定機制就顯得很笨拙了,
即便是很多小PO,也存在其他方法破壞這條固定規則,讓我們看看“Part”,如果在Amanda將長號加入訂單時,有人更改了長號的價格,這不也會破壞固定規則嗎?
我們試著除了鎖定整個PO之外,也鎖定Part,當George、Amanda和Sam在不同PO上作業時將會發生的情況,

作業變得越來越麻煩,因為在Part上出現了很多爭用的情況,這樣就會發生圖6-10中的結果:3個人都需要等待,
現在我們可以開始改進模型,在模型中加入以下業務知識,
(1) Part在很多PO中使用(會產生高競爭),
(2) 對Part的修改少于對PO的修改,
(3) 對Price(價格)的修改不一定要傳播到現有PO,它取決于修改價格時PO處于什么狀態,

當考慮已經交貨并存檔的PO時,第三點尤為明顯,它們顯示的當然是填寫時的價格,而不是當前價格,

這個模型得到的實作可以確保滿足PO和采購項相關的固定規則,同時,修改部件的價格將不會立即影響參考部件的采購項,涉及面更廣的規則可以通過其他方式來滿足,例如,系統可以每天為用戶列出價格過期的采購項,這樣用戶就可以決定是更新還是去掉采購項,但這并不是必須一直保持的固定規則,通過減少采購項對Part的依賴,可以避免爭用,并且能夠更好地反映出業務的現實情況,同時,加強PO與采購項之間的關系可以確保遵守這條重要的業務規則,
AGGREGATE強制了PO與采購項之間符合業務實際的所屬關系,PO和采購項的創建及洗掉很自然地被聯系在一起,而Part的創建和洗掉卻是獨立的,
AGGREGATE劃分出一個范圍,在這個范圍內,生命周期的每個階段都必須滿足一些固定規則,接下來要討論的兩種模式FACTORY和REPOSITORY都是在AGGREGATE上執行操作,它們將特定生命周期轉換的復雜性封裝起來……
個人理解:完全沒看懂,好像是建模處理資源競爭的問題,
FACTORY
當創建一個物件或創建整個AGGREGATE時,如果創建作業很復雜,或者暴露了過多的內部結構,則可以使用FACTORY進行封裝,
物件的功能主要體現在其復雜的內部配臵以及關聯方面,我們應該一直對物件進行提煉,直到所有與其意義或在互動中的角色無關的內容被完全剔除為止,一個物件在它的生命周期中要承擔大量職責,如果再讓復雜物件負責自身的創建,那么職責過載將會導致問題,
汽車發動機是一種復雜的機械裝臵,它由數十個零件共同協作來履行發動機的職責——使軸轉動,我們可以試著設計一種發動機組,讓它自己抓取一組活塞并塞到汽缸中,火花塞也可以自己找到插孔并把自己擰進去,但這樣組裝的復雜機器可能沒有我們常見的發動機那樣可靠或高效,相反,我們用其他東西來裝配發動機,或許是機械師,或者是工業機器人,無論是機器人還是人,實際上都比二者要裝配的發動機復雜,裝配零件的作業與使軸旋轉的作業完全無關,只是在生產汽車時才需要裝配工,我們駕駛時并不需要機器人或機械師,由于汽車的裝配和駕駛永遠不會同時發生,因此將這兩種功能合并到同一個機制中是毫無價值的,同理,裝配復雜的復合物件的作業也最好與物件要執行的作業分開,
正如物件的介面應該封裝物件的實作一樣(從而使客戶無需知道物件的作業機理就可以使用物件的功能),FACTORY封裝了創建復雜物件或AGGREGATE所需的知識,它提供了反映客戶目標的介面,以及被創建物件的抽象視圖,
應該將創建復雜物件的實體和AGGREGATE的職責轉移給單獨的物件,這個物件本身可能沒有承擔領域模型中的職責,但它仍是領域設計的一部分,提供一個封裝所有復雜裝配操作的介面,而且這個介面不需要客戶參考要被實體化的物件的具體類,在創建AGGREGATE時要把它作為一個整體,并確保它滿足固定規則,
FACTORY有很多種設計方式,[Gamma et al.1995]中詳盡論述了幾種特定目的的創建模式,包括FACTORY METHOD(工廠方法)、ABSTRACT FACTORY(抽象工廠)和BUILDER(構建器),該書主要研究了適用于最復雜的物件構造問題的模式,本書的重點并不是深入討論FACTORY的設計問題,而是要表明FACTORY的重要地位——它是領域設計的重要組件,正確使用FACTORY有助于保證MODEL-DRIVEN DESIGN沿正確的軌道前進,
任何好的工廠都需滿足以下兩個基本需求
(1) 每個創建方法都是原子的,而且要保證被創建物件或AGGREGATE的所有固定規則,FACTORY生成的物件要處于一致的狀態,在生成ENTITY時,這意味著創建滿足所有固定規則的整個AGGREGATE,但在創建完成后可以向聚合添加可選元素,在創建不變的VALUE OBJECT時,這意味著所有屬性必須被初始化為正確的最終狀態,如果FACTORY通過其介面收到了一個創建物件的請求,而它又無法正確地創建出這個物件,那么它應該拋出一個例外,或者采用其他機制,以確保不會回傳錯誤的值,
(2) FACTORY應該被抽象為所需的型別,而不是所要創建的具體類,[Gamma et al.1995]中的高級FACTORY模式介紹了這一話題
個人理解:這個我懂了,物件的創建如果非常復雜(要組裝或者初始化很多引數,規則一定)就可以使用工廠模式,技術開發人員應該非常了解工廠模式,業務不需要知道啥是工廠模式,
總結:沒看懂,這部分更加注重程式物件和資料庫的設計,還有就是模型的生命周期,好好了解下設計模式和資料庫設計范式應該就夠了,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/1052.html
標籤:領域驅動設計
