道可道,非常道. 名可名,非常名. 無名天地之始,有名萬物之母. 老子 關於標題 好久沒寫東西了,動筆的動機是看完了一本書,想寫點總結性的東西,一是為了回顧一下梳理知識點,二是為了日後遺忘時能有個簡短的文章幫助自己快速回憶. 標題看著像上學時寫的作文題目,原本想找個高大上的題目好拉拉人氣,但關於領域 ...
道可道,非常道.
名可名,非常名.
無名天地之始,有名萬物之母.
---老子
關於標題
好久沒寫東西了,動筆的動機是看完了一本書,想寫點總結性的東西,一是為了回顧一下梳理知識點,二是為了日後遺忘時能有個簡短的文章幫助自己快速回憶.
標題看著像上學時寫的作文題目,原本想找個高大上的題目好拉拉人氣,但關於領域驅動設計我也才處在守破離
的守
得級別,還是老老實實用個規矩的標題吧.
核心內容
作者在書中反覆強調領域通用語言(Ubiquitous Language)的重要性,依據領域通用語言剝離出核心領域(Core Domain),隨著業務的發展和技術人員對領域知識的學習,不斷的調整領域通用語言,從而提煉核心領域.在這個閉環中,有這麼幾點很重要:
溝通
項目組的成員要保證溝通的態度和效率.大家(包括並不限於開發,測試,需求,客戶)願意圍繞業務知識進行溝通.有效的溝通能夠得出領域通用語言,反饋認知的偏差,編寫更接近自然語言的代碼.
重構
項目組的成員要勇於挑戰,勇於糾錯,勇於提煉核心領域知識.只有這樣才能夠讓核心領域更精煉,普通子域(Generic Subdomain)更內聚,讓有界上下文(Bounded Context)的邊界更清晰.讓眾神歸位各司其職.
一些模式和設計技巧
這是一本寫設計的書,但是作者是站在一個類似項目經理的角度寫的,其關註的重點不在於演算法和數據結構的精妙,而是項目的整體.所以溝通和重構很重要,所有的這些技術相關的模式也多數為項目的推進而服務.目的是讓技術人員寫出誰都能看懂的代碼,寫出擴展性更好的代碼,寫出更接近自然語言的代碼,寫出能夠自解釋的代碼.
總結一下,核心內容就是有效的組織協調人,有效的組織協調代碼,最終成功完成項目.
梳理知識點
不管怎樣作者給出了很多模式,有代碼級別的,作者叫模型驅動設計的構造塊.有管理級別的,作者叫戰略設計.給出的概念更是一堆一堆的.老子說的好,"非常道,非常名",能闡述出來的都不是永恆不變的,但有效的溝通需要我們建立在很多理解一致的概念上,讓我們挨個梳理一下作者的概念吧.
領域通用語言(Ubiquitous Language)
這個概念我也是第一次看到,作者在整個書中時不時要提一下這個概念.其實領域通用語言主要解決的是溝通問題,主要是業務人員和技術人員的溝通問題.不然開會的時候業務人員說自己的,技術人員說自己的,然後還誰都看誰不爽.
其實,這個東西還是讓技術人員妥協了,因為組成領域通用語言的以領域知識的概念為主.當然這也是必然的,除了一些技術性很強的項目(比如搞個雲平臺),多數項目的目的都是為了服務於業務,幫助公司業務部門為公司創造更多的利潤.完成技術人員學習領域知識的過程作者稱之為消化知識.
模型驅動設計(Model-Driven Design)
10年聽說了TDD,其目的是讓每一個功能有一個明確的完成界限.這回又聽一個新東西MDD.模型驅動設計其實就是從模型開始出發,將領域通用語言中的概念映射到代碼上,這樣可以讓代碼直接體現出領域知識.讓技術人員和業務人員的溝通在一件事情上,如果沒有這層映射,他們的溝通必然存在轉換.
其實這裡作者是反對根據功能點開發的流程的.比如很多項目的業務模型體現在資料庫上,而一個http請求過來後,順序的調用 control,service,dao最終在資料庫中體現模型內容的變化.作者更期望的是一種面向語義化得開發.
比如一個用戶加另一個用戶好友,前面那一種的代碼可能是這樣的:
interface UserService{
void addFriend(User user1, User user2);
}
到底是user1加user2好友,還是user2加user1好友,調用該介面的程式員必須去看實現,要不就是實現的程式員要把實現細節在介面上的註釋里寫明白.
而後面那種設計的代碼可能是這樣的:
interface User{
void addFriend(User user);
}
代碼的自解釋在這裡體現了出來.
模型驅動設計將很多具體實現都交給了模型來完成,讓計算更接近數據.
手工建模(Hands-On Modeler)
還是為瞭解決信息傳遞,建模人員必須參與開發,防止模型圖沒有很好的傳遞設計意圖和設計細節.一定要用代碼體現出模型.要做能編碼的設計師.
分層架構(Layered Archiecture)
分層在領域驅動開發中也是有用武之地的,分層可以隔離各個子領域,能夠讓模型更內聚.作者給出了一個分層建議:
-----------------------------------
用戶界面層(或表示層)
-----------------------------------
應用層
-----------------------------------
領域層(或模型層)
-----------------------------------
基礎設施層
-----------------------------------
智能用戶界面"反模式"(The Smart UI "Anti-Pattern")
這是一個和領域驅動設計相反的設計方法,其主要特點就是展現層直接和資料庫交互.
優點是可以快速完成功能開發.
缺點是可維護性較差.
實體(Entity)
有唯一ID,能夠反映領域模型.最重要的是要有業務邏輯.
值對象(Value Object)
沒有唯一ID,能夠反映領域模型,也要有業務邏輯.與實體最大的區別是兩個相同對象比較時的邏輯,兩個實體只要ID相同,我們就認為他們指向了同一個對象;而值對象只要所有屬性相同,我們就認為指向了同一個對象.
服務(Service)
好的Service有如下三個特征:
- 與領域概念相關的操作不是ENTITY或VALUE OBJECT的一個自然的部分.
- 介面是根據模型的其他元素定義的.
- 操作是無狀態的.
模塊(Module)
高內聚低耦合
是軟體設計的一條原則.用作者的話說:一個人一次考慮的事情是有限的,因此才有低耦合.不連貫的思想和一鍋粥似的思想同樣難於理解,因此才有高內聚.
劃分模塊就是遵循該原則,每個模塊包含一個內聚的概念集合.並儘可能把實現單一概念對象的所有代碼放到同一個模塊中.
聚合(Aggregate)
聚合主要用來封裝對象的內部結構.其作用就是將關聯的對象聚合在一起,由一個對象對外暴露遍歷介面.這樣能夠降低對這組關聯對象的維護的複雜度.每個聚合都要有一個根(root)和一個邊界(boundary).
為了實現概念上的聚合根,需要對所有事物應用一組規則.
- 根實體具有全局標識,它最終負責檢查固定規則.
- 根實體具有全局標識.邊界內的實體具有本地標識,這些標識只有在聚合根內部才唯一.
- 聚合外部的對象不能引用除根實體之外的任何內部對象.根實體可以把對內部實體的引用傳遞給它們,但這些對象只能臨時使用這些引用,而不能保持引用.根可以把值對象的副本傳遞給另一個對象,而不必關心它發生什麼變化,因為它只是一個值對象,不再與聚合根有任何關聯.
- 作為上一條規則的推論,只有聚合的根才能直接通過資料庫查詢獲取.所有其他對象必須通過關聯的遍歷才能找到.
- 聚合內部的對象可以保持對其他聚合根的引用.
- 刪除操作必須一次刪除聚合之內的全部對象.
- 當提交對聚合邊界內部的任何對象的修改時,整個聚合中的所有固定規則都必須被滿足.
工廠(Factory)
工廠用來創建和重建複雜對象.對象的構造方法原本就可以完成對象的創建,但是如果對象比較複雜,創建方法就會和對象的行為方法混在一起,這時就不如將對象的創建邏輯放到工廠中來完成.這樣就講對象創建的邏輯抽離出來,而對象去關係自己的業務邏輯.
好的工廠都需要滿足如下兩個基本需求:
- 每個創建方法都是原子方法,而且滿足被創建對象或聚合的所有固定規則.工廠應該以一致的狀態來生成對象.如果工廠通過其介面收到了一個創建對象的請求,而它又無法正確地創建出對象,那麼它應該拋出一個異常,或者調用某種其他機制,以確保不會返回錯誤的值.
- 工廠應該被抽離為所需的類型,而不是創建出具體的類.
沒必要讓多有類的創建都用工廠,不複雜或客戶關係實現的可以直接用構造方法.
資源庫(Repository)
將所有對象存儲和訪問操作交給資源庫來完成,而對於聚合一定控制只能從聚合根開始訪問遍歷.
使用資源庫有很多好處:
- 它們為客戶提供了一個簡單的模型,可以來獲取持久對象並管理它們的生命周期.
- 它們使應用程式與持久化技術解耦.
- 它們體現了有關對象訪問的設計決策.
- 可以很容易將它們替換為"啞實現",以便在測試中使用.
釋意介面(Intention-Revealing Interfaces)
這個模式其實主要解決的還是溝通問題.強調要面向介面設計,而且介面及參數的命名一定要表達出意思來,這樣調用者不會迷糊.還是那句話,寫出人能看懂的代碼.
無副作用函數(Side-Effect-Free Function)
這個模式強調一些值對象的操作一定是無副作用的.所謂無副作用就是,一個地方調用該方法,不會導致其他引用該對象的地方調用該方法得到不同的結果.作者用顏色這個值對象做了示範,其實Java中的BigDecimal里的運算方法都是無副作用的.
斷言(Assertion)
對於這章的內容,我的理解是針對無副作用的函數增加單元測試,用於保障函數無歧義.作者也說瞭如果語言支持斷言,可以直接在程式中增加斷言.Java有個關鍵字assert可以來做斷言,但是這個關鍵字要生效需要啟動程式是增加JVM參數-ea
,多數情況下Java除測試的程式外很少使用.
概念輪廓(Conceptual Contour)
這個模式就是讓相關的操作,類,介面,聚合 放到一起,使其更內聚,讓其內部更加穩定.
獨立類(Standalone Class)
這個模式強調低耦合,防止對象互相依賴,要講對象無關的操作放到對象外.讓理解對象更容易.
閉合操作(Closure of Operation)
所謂閉合操作,就是方法的返回值和參數類型相同,而且一般閉合操作的都是值對象.
聲明式設計
其實這章作者描述的就是語義化編程,讓程式編寫更接近自然語言,讓程式能夠自解釋.比如下麵這段代碼
Specification ventilatedType1 = new ContainerSpecification(VENTILATED_TYPE_1);
Specification ventilatedType2 = new ContainerSpecification(VENTILATED_TYPE_2);
Specification either = ventilatedType1.or(ventilatedType2);
策略模式(Strategy 也稱Policy)
雖然標題叫策略模式,其實作者是想表達過去的很多設計模式也很適合領域設計.比如策略模式,能夠很好的將對象的多態表述出來.
組合模式(Composite)
作者介紹了組合模式在領域設計中的使用,拿航線做了示例.
戰略設計
戰略設計在我看來更多的是規劃開發.通過一些模式或方法來確定工作重心,並將資源花在核心上.
(戰略設計很多名稱譯者也沒給出中文名,我也沒找到中文譯名,就直接用英文了)
Bounded Context
這個模式就是告訴我們一定要劃清模型邊界,在不同的模型中有不同的上下文,而這些領域知識僅限於當前的上下文,而模型的邊界很重要,邊界清晰,能夠降低溝通成本,而且能夠很好的安排工作.
持續集成(Continuous Integration)
如果模塊劃分的多,或者團隊大,及時邊界劃分的清晰,溝通成本也是很高的.而集成成本也就體現出來了,儘早的集成能夠跟早的發現問題.而集成不僅有代碼的集成,還有概念的集成.
多數集成有如下特征:
- 分步集成,採用可重覆使用的合併/構建技術.
- 自動測試工具.
- 概念集成.
- 在討論模型和應用程式時堅持使用通用語言.
Context Map
模塊要劃清邊界,但是模塊間要互相調用和通信的,這就需要有一個 Context Map 來對模塊間的對象進行相互轉換.
Shared Kernel
為解決Context Map,最簡單的辦法就是共用核心,這樣就不存在模型間的轉換了.但前提是能夠共用,而且有專人在維護這個核心.
Customer/Supplier
如果兩個模塊間存在上下游關係,而且下游有更大的決策權,那麼這種Customer/Supplier模式就比較適合.
這個模式有兩個關鍵要素:
- 關係必須是客戶與供應商的關係,其中客戶需求是至關重要的,由於下游團隊並不是唯一的客戶,因此不同客戶的要求必須通過協商類平衡,但這些要求都是非常重要的.這種關係與通常出現的
窮親戚
關係相反,在後者下游團隊不得不祈求上游團隊滿足其需求. - 必須有一個自動化測試工具,使上游團隊在修改代碼時,不必擔心破壞下游團隊的工作,並能夠使下游團隊能夠專註於自己的工作,而不用總是密切註意著上游的行動.
作者用接力賽來比喻這個模式,接棒的人要相信上一棒能準確的傳到自己手裡.
Conformist
當兩個開發團隊具有上下游關係時,如果上游團隊沒有動機來滿足下游團隊的需求,那麼下游團隊將無能為力.處於利他主義的考慮,上游開發人員可能會做出承諾,但他們可能不會履行承諾.下游團隊處於良好的意願會相信這些承諾,從而根據一些永遠不會實現的特性來制定計劃.下游項目只能被擱置,直到團隊最終學會利用現有條件自力更生為止.下游團隊不會得到根據他們的需求而量身定做的介面.
這是一個現實存在的問題,團隊間溝通總是存在障礙,尤其上游的KPI內容和服務下游無關時.作者���訴我們如果下游是弱勢,最簡單和省時間的方式就是成為追隨者,順著上游的介面開發.
Anticorruption Layer
如果追隨者發現,如果順著上游的介面開發會破壞自己模型結構,或者讓自己的模型混亂時.那就請使用Anticorruption Layer
.創建一個隔離層,這個層通過現有介面與另一個系統進行通信.而且適配器模式(Adapter)和外觀模式(Facade)能很好的完成這個隔離層架構.創建隔離層還有個好處就是外部介面發現變化後,只需要修改隔離層代碼就好,不會影響到核心代碼.
Separate Way
作者說這是個模式,好吧,這是個模式,這個模式告訴我們:如果集成的性價比不高,那就不要集成,下游獨立開發自己需要的功能.
Open Host Service
讀完這節讓我想到了幾年前吵得火熱的面向服務編程,也有點像現在的微服務框架.就是說上游將自己的服務標準化,提供統一的協議和穩定的介面.這樣下游團隊只需要根據上游團隊提供的文檔就能夠集成.
Published Language
這個公共語言其實就是說,如果我們開發的軟體已經有一套行業標準術語了,那就直接拿來作為我們的通用語言就好,沒必要再自定義一套.
精煉
我感覺精煉的目的就是梳理業務,讓核心,核心起來來.
領域模型的戰略精煉包括以下部分:
- 幫助所有團隊成員掌握系統的總體設計及協調.
- 找到一個具有濕度規模的核心模型並把它天津到通用語言中,從而促進溝通.
- 指導重構.
- 專註於模型中最有價值的那部分.
- 知道外包,現成組件的使用以及任務委派.
Core Domain
一個嚴峻的現實是我們不可能對又有設計進行同等的精化,而是必須分出優先順序.為了使領域模型成為有價值的資產,必須整齊地梳理出模型的真正核心,並完全根據這個核心來創建應用程式的功能.但本來就稀缺的高水平開發人員往往會把工作重點放在技術基礎設施上,或者支取解決那些不需要專門領域知識就能解決的領域問題.
因此,對模型進行提煉.找到Core Domain 並提供一種易於區分的方法把它與那些起輔助作用的模型和代碼分開.最有價值和最專業的概念要輪廓分明.儘量壓縮Core Domain.
讓最有才能的人來開發Core Domain ,並相應地招募新人來補充這些人空出來的位置.在Core Domain中努力開發能夠確保實現系統藍圖的深層模型和柔性設計.仔細判斷任何其他不得投入,看它是否能夠支持這個提煉出來的Core.
一句話,好鋼用在刀刃上.
找到重要的是什麼,讓重要的事重要起來,投入資源在重要的事上.
Generic Subdomain
除了核心領域外,一些輔助的子領域也要抽離出來,雖然子領域不是最重要的,但是確是整個軟體需要的.對於子領域的開發方式也可以有多種選擇:
現成的解決方案
有時可以購買一個已實現的解決方案,或使用開源代碼.
優點
- 可以減少代碼的開發.
- 維護負擔轉移到外部.
- 代碼已經在很多地方使用過,可能較為成熟,因此比自己開發的代碼更可靠和完備.
缺點
- 在使用之前,仍需要花時間來評估和理解它.
- 就業內目前的質量控制水平而言,無法保證它的正確性和穩定性.
- 它可能設計的過於細緻了(遠遠超出了你的目的), 集成的工作量可能比開發一個最小化的內容實現更大.
- 外部元素的集成常常不順利.它可能有一個與你的項目完全不同的Bounded Context.即使不是這樣,它也很難順利的引入你的其他軟體包中的Entity.
- 它可能會引入平臺,編譯器版本依賴.
公開發佈的設計或模型
優點
- 比自己開發的模型更為成熟,並且反映了很多人的深層知識.
- 提供了隨時可用的高質量文檔.
缺點
- 可能不是很符合你的需要,或者設計得過於細緻了(遠遠超出了你的需要).
把實現外包出去
優點
- 使核心團隊可以脫身去處理Core Domain,這是需要知識和經驗積累的部分.
- 開發工作的增加不會使團隊規模無限擴大下去,同時又不會導致Core Domain知識的分散.
- 強制團隊採用面向介面設計,並且有助於保持子領域的通用性,因為規格已經被傳遞到外部.
缺點
- 仍需要核心團隊花費一些時間,因為他們需要與外包人員商量介面,編碼標準和其他重要方面.
- 當把代碼的所屬權交回團隊時,團隊需要耗費大量精力來理解這些代碼.(但是這個開銷比理解專用子領域要小一些,因此通用子領域不需要理解專門的背景知識.)
- 代碼質量或高或低,這取決於兩個團隊能力的高低.
自動化測試在外包中可能起到重要作用.需要邀請外包人員為他們的交付的代碼提供單元測試.
內部實現
優點
- 易於集成
- 只開發自己需要的,不做多餘的工作.
- 可以臨時把工作分包出去.
缺點
- 需要承受後續的維護和培訓負擔.
- 很容易低估開發這些軟體包所需要的時間和成本.
Domain Vision Statement
這一節將的有點像目標管理.領導者需要在核心領域確定前,找好項目目標,也就是項目的核心價值,這有助於我們的工作不會偏離方向.
寫一份Core Domain的簡短描述以及它將會創造的價值(大約一頁紙),也就是"價值主張".那些不能將你的領域模型與其他領域模型區分開的方面就不要寫了.展示出領域模型是如何實現和均衡各方利益的.這份���述要儘量精簡.儘早把它寫出來,等到獲得新的理解後再修改它.
Highlighted Core
讓重要的重要起來.為了強調核心領域,作者給了個方法,就是寫一個精煉文檔(3~7頁,每頁內容不必太多),用於描述核心領域和核心元素之間的主要交互過程.
寫這個文檔同時會出現這些風險:
- 文檔可能得不到維護.
- 文檔可能沒人閱讀.
- 由於有多個信息來源,文檔可能達不到簡化複雜性的目的.
但是沒關係,有了它才能讓整個團隊有一個明確的目標.就像茫茫大海中的燈塔.
Cohesive Mechanism
計算有時會非常複雜,使設計開始變得膨脹.機械性的"如何做"大量增加,而把概念性的"做什麼"完全掩蓋.為瞭解決問題提供演算法的大量方法掩蓋了那些用於表達問題的方法.
因此,把概念上的Cohesive Mechanism(內聚機制)分離到一個單獨的輕量級框架中.要特別註意公式演算法或那些有完備文檔的演算法.用一個Intention-Revealing Interface來公開這個框架的功能.現在,領域中的其他元素就可以只專註於如何表達問題(做什麼)了,而把解決方案的複雜細節(如何做)轉移給了框架.
還是那句話:寫出自解釋的代碼.
Segregated Core
模型中的元素可能有一部分屬於Core Domain, 而另一部分起支持作用.核心元素可能與一般元素緊密耦合在一起.Core的概念內聚性可能不是很強,看上去也不明顯.這種混亂性和耦合關係抑制了Core的分離.設計人員如果無法清晰地看到最重要的關係,就會開發出一個脆弱的設計.
因此,對模型進行重構,把核心概念從支持性元素(包括定義得不清楚的那些元素)中分離出來,並增強Core的內聚性,同時減少它與其他代碼的耦合.把所有通用元素或支持性元素提取到其他對象中,並把這些對象放到其他的包中-即使這會把一些緊密耦合的元素分開.
通過重構得到Segregated Core的一般步驟如下:
- 識別出一個核心子領域(可能是從精煉文檔中得到的).
- 把相關的類移到新的模塊中,並根據與這些類有關的概念為模塊命名.
- 對代碼進行重構,把那些不直接表示概念的數據和功能分離出來.把分離出來的元素放到其他包的類(可以是新的類)中.儘量把它們與概念上相關的任務放到一起,但不要為了追求完美而浪費太長時間.把註意力放在提煉核心子領域上,並且使核心子領域對其他包的引用變得更明顯且易於理解.
- 對新的Segregated Core Module 進行重構,使其中的核心子領域對其他包的引用變得更明顯且易於理解.
- 對新的Segregated Core Module 進行重構,使其中的關係和交互變得更簡單,表達更清楚,並且最大限度的減少並澄清它與其他模塊的關係(這是後續重構的目標).
- 對另一個核心子領域重覆這個過程,直到完成Segregated Core 的工作.
Abstract Core
當不同模塊的子領域之間有大量交互時,要麼需要在模塊之間創建很多引用(在很大程度上抵消了劃分模塊的價值),要麼就必須間接的實現這些交互,而後者會使模型變得晦澀難懂.
因此,把模型中最基本的概念識別出來,並分離到不同的類,抽象類或介面中.設計這個抽象模型,使之能夠表達出重要組件之間的大部分交互.把這個完整的抽象模型放到它自己的模塊中,而專用的詳細的實現類則留在由子領域定義的模塊中.
作者還強調:儘管任何一次突破都會得到一個有價值的深層次模型,但只有Core Domain中的突破才能改變整個項目的軌道.
大比例機構
大比例結構這部分內容我就看得暈暈乎乎,不能明確的表述作者所雲了,隱隱感覺作者就是在強調模塊的職責要劃清.的確,很多時候模塊間的功能和作用是相互糾纏在一起的.
由於自己也沒有很好的理解作者所述,就摘抄一些內容了.
Evolving Order
應該允許這種概念上的大比例結構隨著應用程式一起演變,甚至可以變成一種完全不同的結構風格.有些設計決策和模型決策必須在掌握了詳細知識之後才能確定,這樣的決策不必過早地指定.
當發現一種大比例結構可以明顯使系統變得更清晰,而又沒有為模型開發施加一些不自然的約束時,就應該採用這種結構.使用不合適的結構還不如不使用它,因此最好不要為了追求設計的完整性而勉強去使用一種結構,而應該找到能夠最精簡地解決所出現問題的方案.要記住寧缺毋濫的原則.
System Metaphor
軟體設計往往非常抽象且難於掌握.開發人員和用戶都需要一些切實可行的方式來理解系統,並共用系統的一個整體視圖.
因此,當系統的一個具體類比正好符合團隊成員對系統的想象,並且能夠引導他們向著一個有用的方向進行思考時,就應該把這個類比用作一種大比例結構.圍繞這個隱喻來組織設計,並把它吸收到統一語言中.System Metaphor應該能促進系統的交流,又能指導系統的開發.它可以增加系統不同部分之間的一致性,甚至可以跨越不同的有界上下文.但所有隱喻都不是完全精確的,因此應不斷檢查隱喻是否過度或不恰當,當發現它起到妨礙作用時,要隨時準備放棄它.
Responsibility Layer
如果每個對象的職責都是手工分配的,將沒有統一的指導原則和一致性,也無法把領域作為一個整體來處理.為了保持大模型的一致,有必要在職責分配上實施一定的結構化控制.
因此,註意觀察模型中的概念依賴性,以及領域中不同部分的變化頻率和變化原因.如果在領域中發現了自然的層次結構,就把它們轉換為主要的抽象職責.這些職責應該描述了系統的高級目的和設計.對模型進行重構,使得每個領域對象 聚合和模型的職責都清晰地位於一個職責層當中.
Knowledge Level
如果在一個應用程式中,實體的角色和它們之間的關係在不同的情況下有很大變化,那麼複雜性會顯著增加.在這種情況下,無論是一般的模型還是高度定製的模型,都無法滿足用戶的需求.為了兼顧各種不同的情形,對象需要引用其他的類型,或者需要具備一些在不同情況下包括不同使用方式的屬性.具有相同數據和行為的類可能會大量增加,而這些類的唯一作用只是為了滿足不同的組裝規則.
因此,創建一組不同的對象,用它們來描述和約束基本模型的結構和行為.把這些對象分為兩個"級別",一個是非常具體的級別,另一個級別則提供了一些可供用戶或超級用戶定製的規則和知識.
Pluggable Component
當很多應用程式需要進行互操作時,如果所有應用程式都基於相同的一些抽象,但它們是獨立設計的,那麼在多個有界上下文之間的轉換會限制它們的集成.各個團隊之間如果不能緊密地協作,就無法形成一個 Shared Kernel.重覆和分裂將會增加開發和安裝的成本,而且互操作會變得很難實現.
因此,從介面和交互中提煉出一個Abstract Core,並創建一個框架,這個框架要允許這些介面的各種不同實現被自由替換.同樣,無論是什麼應用程式.只要它嚴格地通過Abstract Core的介面進行操作,那麼就可以允許它使用這些組件.
寫在最後
實踐是檢驗真理的唯一標準.再好的理論只有用起來才有價值,等到熟練了,就可以嘗試著去突破作者畫的圈圈.
看山是山,看水是水;
看山不是山,看水不是水;
看山還是山,看水還是水。
---某個和尚