(1)模型和實現的綁定算法
(2)創建了一種基於模型的語言數據庫
(3)開發一個蘊含豐富知識的模型編程
(4)提煉模型設計模式
(5)頭腦風暴和實驗安全
【學習心得】:千萬不要用本身有限的思惟規劃完整的圖形,持續學習、消化、輸出(討論)、沉澱,全部道理都是一致的。服務器
想要建立種靈活的、蘊含豐富知識的設計,須要一種通用的、共享的團隊語言,以及對語言不斷的試驗——然而,軟件項目上不多出現這樣的試驗。架構
若是語言支離破碎,項目必將遭遇嚴重問題。領域專家使用他們本身的術語,而技術團隊所使用的語言則通過調整,以便從設計角度討論領域。平常討論所使用的術語與代碼(軟件項目的最重要產品)中使用的術語不一致,甚至同一我的在講話和寫東西時使用的言語也不一致,這致使的後果是,對領域的深入表達經常稍縱即逝,根本沒法記錄到代碼或文檔中。翻譯使得溝通不順暢,並削弱了知識消化。然而任何一方的語言都不能成爲公共語言,由於它們沒法知足全部的需求。框架
【學習心得】:在本身有限的項目經驗裏,說溝通成本佔據項目總成本的八成都不爲過,就像本書一開始的重點,就是無處不在的語言。這語言能夠是人話、能夠是圖形、能夠是表格,重點在於能夠幫助項目高質量高效率的落地。這裏引用歌德的一句話:「世界上的誤解和懈怠,也許比奸詐和惡意更誤事」。數據庫設計
嚴格按照基礎模型來編寫代碼,可以使代碼更好地表達設計含義,而且使模型與實際的系統想契合。編程語言
若是整個程序設計或者其核心部分沒有與領域模型相對應,那麼這個模型就是沒有價值的,軟件的正確性也值得懷疑。同時,模型和設計功能之間過於複雜的對應關係也是難於理解的,在實際項目中,當設計改變時也沒法維護這種關係。若分析與設計之間產生嚴重分歧,那麼在分析和設計活動中所得到的知識就沒法彼此共享。
軟件系統各個部分的設計應該忠實地反映領域模型,以便體現出這兩者之間的明確對應關係。咱們應該反覆檢查並修改模型,以便軟件能夠更加天然地實現模型,即便想讓模型反映出更深層次的領域概念時也應如此。咱們須要的模型不但應該知足這種需求,還應該可以支持健壯的UBIQUITOUS LANGUAGE(通用語言)。
從模型中獲取用於程序設計和基本職責分配的術語。讓程序代碼成爲模型的表達,代碼的改變可能會是模型的改變。而其影響勢必要波及接下來相應的項目活動。
【學習心得】:模型、範式與設計的基本認知時候全部溝通的基石,不管是技術人員仍是領域業務人員都有必要對這些知識有一個深刻的理解,切記把本身侷限在本身的細節當中,用人話講就是釘子思惟。對其餘工做小組的認識是一種促進你們更好合做的責任心態度,仍是那句話,用宏觀的視野作微觀的事情。
想要建立出可以處理複雜任務的程序,須要作到關注點分離——使設計中的每一個部分獲得單獨的關注。在分離的同時,也須要維持系統內部複雜的交互關係。
分層的價值在於每一層都只表明程序中的某一特定方面的。這種限制使每一個方面的設計都更具內聚性,更容易理解。
而領域層是模型的精髓。領域模型是一些列概念的集合。「領域層」則是領域模型以及全部與其直接相關的設計元素的表現,他由業務邏輯的設計和實現組成。在MODEL-DRIVEN-DESIGN中,領域層的軟件構造反映出了模型概念。
【學習心得】:分離意味着原始的複雜,這是發展的一個趨勢,技術的進步每每在於精細化的分工,而這種分層的另外一個好處是,分離核心,聚焦問題。
一些對象主要不是由它們的屬性定義的。它們實際上表示了一條「標識線」(A Thread of Identity),這條線跨越時間,並且經常經歷多種不一樣的表示。有時,這樣的對象必須與另外一個具備不一樣屬性的對象相匹配。而有時一個對象必須與具備相同屬性的另外一個對象區分開。錯誤的標識可能會破壞數據。
當一個對象由其標識(而不是屬性)區分時,那麼在模型中應該主要經過標識來肯定該對象的定義。是類定義變得簡單,並集中關注生命週期的連續性和標識。定義一種區分每一個對象的方式,這種方式應該與其形式和歷史無關。要格外注意那些須要經過屬性來匹配對象的需求。在定義標識操做時,要確保這種操做爲每一個對象生成惟一的結果,這能夠經過附加一個保證惟一性的符號來實現。這種定義標識的方法可能來自外部,也多是由系統建立的任意標示符,但它在模型中必須是惟一的標識。模型必須定義「符合什麼條件纔算是相同的事物」。
不少對象沒有概念上的標識,它們描述了一個事務的某種特徵。而這類用於描述領域的某個方面而自己沒有概念標識的對象稱爲VALUE OBJECT(值對象)。
當咱們只關心一個模型元素的屬性時,應把它歸類爲VALUE OBJECT。咱們應該使這個模型元素可以表示出其屬性的意義,併爲它提供相關功能。VALUE OBJECT應該是不可變的。不要爲它分配任何標識,並且不要把它設計成像ENTITY那麼複雜。
有時,對象不是一個事物。在某些狀況下,最清楚、最實用的設計會包含一些特殊的操做,這些操做從概念上講不屬於任何對象。與其把它們強制地歸於哪一類,不如順其天然地在模型中引入一種新的元素,這就是SERVICE(服務)。
所謂SERVICE,它強調的是與其餘對象的關係。與ENTITY和VALUE OBJECT不一樣,它只是定義了可以爲客戶作什麼。SERVICE每每是一個一活動來命名,而不是以一個ENTITY來命名,也就是說,它是動詞而不是名詞。
好的SERVICE有如下3個特徵:
(1)與領域概念相關的操做不是ENTITY或VALUE OBJECT的一個天然組成部分。
(2)接口是根據領域模型的其餘元素定義的。
(3)操做是無狀態的。
當領域中的某個重要的過程或轉換操做不是ENTITY或VALUE OBJECT的天然職責時,應該在模型中添加一個做爲獨立接口的操做,並將其聲明爲SERVICE。定義接口時要使用模型語言,並確保操做名稱是UBIQUITOUS LANGUAGE中的術語。此外,應該使SERVICE成爲無狀態的。
SERVICE與孤立的領域層:這種模式只重視那些在領域中具備重要意義的SERVICE,但SERVICE並不僅是在領域中使用。咱們須要注意區分屬於領域層的SERVICE和那些屬於其餘層的SERVICE,並劃分責任,以便將它們明確地區分開。
MODULE是一個傳統的、較成熟的設計元素。雖然使用模塊有一些技術上的緣由,但主要緣由倒是「認知超載」。MODULE爲人們提供了兩種觀察模型的方式,一是能夠在MODULE中查看細節,而不會被整個模型淹沒,二是觀察MODULE之間的關係,而不考慮其內部細節。
每一個人都會使用MODULE,但卻不多有人它們看成模型中的一個成熟的組成部分。代碼按照各類各樣的類別進行分解,有時是按照技術架構來分割的,有時是按照開發人員的任務分工來分割的。甚至那些從事大量重構工做的開發人員也傾向於使用項目早期造成的一些MODULE。
衆所周知,MODULE之間應該是低耦合的,而在MODULE的內部則是高內聚的。耦合和內聚的解釋使得MODULE聽上去像是一種技術指標,彷彿根據關聯和交互的分佈狀況來機械地判斷它們。然而,MODULE並不只僅是代碼的劃分,並且也是概念的劃分。一我的一次考慮的事情是有限的(所以纔要低耦合)。不連貫的思想和「一鍋粥」似的思想一樣難於理解(所以纔要高內聚)。
所以:選擇可以描述系統的MODULE,並使之包含一個內聚的概念集合。這一般會實現MODULE之間的低耦合,但若是效果不理想,則應尋找一種更改模型的方式來消除概念之間的耦合,或者找到一個能夠做爲MODULE基礎的概念(這個概念先前可能被忽視了),基於這個概念組織的MODULE能夠以一種有意義的方式將元素集中到一塊兒。找到一種低耦合的概念組織方式,從而能夠相互獨立地理解和分析這些概念。對模型進行精化,直到能夠根據高層領域概念對模型進行劃分,同時相應的代碼也不會產生耦合。MODULE的名稱應該是UBIQUITOUS LANGUAGE中的術語。MODULE及其名稱應反映出領域的深層知識。
【學習心得】:每個概念或方法,都有其含義來源和出處。學會尋找信息的源頭,學會給本身的認知指明來源和出處,具有嚴謹的邏輯思惟,科學地學習和認知,是一切成功的基礎。杜絕垃圾二手信息資料,杜絕自我侷限性拍腦殼的認知決策過程。
在具備複雜關聯的模型中,要想保證對象更改的一致性是很困難的。不只互不關聯的對象須要遵照一些固定規則,並且緊密關聯的各組對象也要遵照一些固定規則。然而,過於謹慎的鎖定機制又會致使多個用戶之間毫無心義地互相干擾,從而使系統不可用。
固定規則(invariant)是指在數據變化時必須保持一致性規則,其涉及AGGREGATE成員之間的內部關係。而任何跨越AGGREGATE的規則將不要求每時每刻都保持最新狀態。經過事件處理,批處理或其餘更新機制,這些依賴會在必定時間內得以解決。但在每一個事務完成時,AGGREGATE內部所應用的固定規則必須獲得知足。爲了實現這個概念上的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做爲根,並經過根來控制對邊界內其餘對象的全部訪問。只容許外部對象保持對根對象的引用。對內部成員的臨時引用能夠被傳遞出去,但僅在一次操做中有效。因爲根控制訪問,所以不能繞過它來修改內部對象。這種設計有利於確保AGGREAGATE中的對象知足全部固定規則,也能夠確保在任何狀態變化時AGGREGATE做爲一個總體知足固定規則。
當建立一個對象或建立整個AGGREGATE時,若是建立工做很複雜,或者暴露了過多的內部結果,則能夠使用FACTORY進行封裝。
對象的建立自己能夠是一個主要操做,但被建立的對象並不適合承擔複雜的裝配操做。將這些職責混在一塊兒可能產生難以理解的拙劣設計。讓客戶直接負責建立對象又會使客戶的設計陷入混亂,而且破壞被裝配對象或AGGREGATE的封裝,並且致使客戶與被建立對象的實現之間產生過於緊密的耦合。
所以:應該講建立複雜對象的實例和AGGREGATE的職責轉移給單獨的對象,這個對象自己可能沒有承擔領域模型中的職責,但它還是領域設計的一部分。提供一個封裝全部複雜裝配操做的接口,並且這個接口不須要客戶引用要被實例化的對象的具體類。在建立AGGREGATE時要把它做爲一個總體,並確保它知足固定規則。
在全部持久化對象中,有一小部分必須經過基於對象屬性的搜索來全局訪問。當很難經過遍歷方式來訪問某些AGGREGATE根的時候,就須要使用這種訪問方式。它們一般是ENTITY,有時是具備複雜內部結構的VALUE OBJECT,還多是枚舉VALUE。而其餘對象則不宜使用這種方式,由於這會混淆它們之間的重要區別。隨意的數據庫查詢會破壞領域對象的封裝和AGGREGATE。技術基礎設施和數據庫訪問機制的暴露會增長客戶的複雜度,並妨礙模型驅動的設計。
REPOSITORY是一個簡單的概念框架,它可用來封裝這些解決方案,並將咱們的注意力從新拉回到模型上。REPOSITORY將某種類型的全部對象表示爲一個概念集合(一般是模擬的)。它的行爲相似於集合(collection),只是具備更復雜的查詢功能。
所以:爲每種須要全局訪問的對象類型建立一個對象,這個對象至關於該類型的全部對象在內存中的一個集合的「替身」。經過一個衆所周知的全局接口來提供訪問。提供添加和刪除對象的方法,用這些方法來封裝在數據存儲中實際插入或刪除數據的操做。提供根據具體條件來挑選對象的方法,並返回屬性值知足查詢條件的對象或對象集合(所返回的對象是徹底實例化的),從而將實際的存儲和查詢技術封裝起來。只爲那些確實須要直接訪問的AGGREGATE根提供REPOSITORY。讓客戶始終聚焦於模型,而將全部對象的存儲和訪問操做交給REPOSITORY來完成。
FACTORY與REPOSITORY的關係是:FACTORY負責處理對象生命週期的開始,而REPOSITORY幫助管理生命週期的中間和結束。從領域驅動設計的角度來看,FACTORY和REPOSITORY具備徹底不一樣的職責。FACTORY負責製造新對象,而REPOSITORY負責查找已有對象。
【學習心得】:有時候學習上的困難不是由於本身的理解能力差,而是缺少必定的基礎溝通語言。急於求成和半路出家的問題就在於基礎的不紮實,也就是咱們所說的野路子。我曾經也會認爲用到了再來學,這都是技術圈子的一個悖論。就好像等本身須要用錢了再來理財同樣好笑。
略
通常來講,持續重構讓事物逐步變得有序。代碼和模型的每一次精化都讓開發人員有了更加清晰的認識。這使得理解上的突破成爲可能。以後,一系列快速的改變獲得了更符合用戶須要並更加切合實際的模型。其功能性及說明性急速加強,而複雜性卻隨之消失。這種突破不是某種技巧,而是一個事件。它的困難之處在於你須要判斷髮生了什麼,而後再決定如何處理。
當突破帶來更深層次的模型時,一般會使人感到不安。與大部分重構相比,這種變化的回報更多,風險也更高。並且突破出現的時機可能很不合時宜。儘管咱們但願進展順利,但每每事與願違。過渡到真正的深層次模型須要從根本上調整思路,而且對設計作大幅修改。在不少項目中,建模和設計工做最重要的進展都來自於突破。
不要試圖去製造突破,那隻會使項目陷入困境。一般,只有在實現了許多適度的重構後纔有可能出現突破。在大部分時間裏,咱們都在進行微小的改進,而在這種連續的改進中模型深層含義也會逐漸顯現。
要爲突破作好準備,應專一於知識消化過程,同時也要逐漸創建健壯的UBIQUITOUS LANGUAGE。尋找那些重要的領域概念,並在模型中清晰地表達出來。精化模型,使其更具備柔性。提煉模型。利用這些更容易掌握的手段使模型變得更清晰,這一般會帶來突破。
不要猶豫着不去作小的改進,這些改進即便脫離不開常規的概念框架,也能夠逐漸加深咱們對模型的理解。不要由於好高騖遠而使項目陷入困境。只要隨時注意可能出現的機會就夠了。
【學習心得】:我本身手頭上目前持有一個運行了近十年的千萬級用戶系統,至今還在持續運營和追加功能。對於以上那些突破的感覺,我太有體會了。整個項目總共經歷了兩次大的突破,以及無數次小突破。而每一次突破都十分痛苦,但快樂着。離不開團隊的堅持,離不開團隊的持續學習,更離不開團隊吃苦精神。
深層建模聽起來很不錯,可是咱們要如什麼時候間它呢?深層模型之因此強大是由於它包含了領域的核心概念和抽象,可以以簡單靈活的方式表達出基本的用戶活動、問題以及解決方案。
若開發人員識別出設計中隱含的某個概念或者在討論中收到啓發而發現一個概念時,就會對領域建模和響應的代碼進行許多轉換,在模型中加入一個或多個對象或關係,從而將此概念顯示地表達出來。有時,這種從隱式概念到顯示概念的轉換可能就是一次突破。
有沒有一些術語可以簡潔地表達出複雜的概念?他們有沒有糾正過你的用詞(也許是很委婉的提醒)?當你使用某個特定詞語時,他們臉上是否已經再也不流露出迷惑的表情?這些都是暗示了某個概念也許能夠改進模型。
你所須要的概念並不老是浮在表面上,也毫不僅僅是經過對話和文檔就能讓它顯現出來。有些概念可能須要你本身去挖掘和創造。要挖掘的地方就是設計中最不足的地方,也就是操做複雜且難於理解的地方。每當有新需求時,彷佛都會讓這個地方變得更加複雜。有時,你很難意識到模型中丟失了什麼概念。也許你的對象可以實現全部的功能,可是有些職責的實現卻很笨拙。而有時,你雖然可以意識到模型中丟失了某些東西,可是卻沒法找到解決方案。
因爲經驗和需求的不一樣,不一樣的領域專家對一樣的事情會有不一樣的見解。即便是同一我的提供的信息,仔細分析後也會發現邏輯上不一致的地方。在挖掘程序需求的時候,咱們會不斷遇到這種使人煩惱的矛盾,但它們也爲深層模型的實現提供了重要線索。有時矛盾只是術語說法上的不一致,有些則是因爲誤解而產生的。但還有一種狀況是專家們會給出相互矛盾的兩種說法。
在尋找模型概念時,不要忽略一些顯而易見的資源。在不少領域中,你均可以找到解釋基本概念和傳統思想的書籍。你依然須要與領域專家合做,提煉與你的問題相關的那部分知識,而後將其轉化爲適用於面向對象軟件的概念。可是,查閱書籍也許可以使你一開始就造成一致且深層的認識。
並非全部這些方向性的改變都毫無用處。每次改變都會把開發人員更深入的理解添加到模型中。每次重構都使設計變得更靈活且爲那些可能須要修改的地方作好準備。咱們其實別無選擇。只有不斷嘗試才能瞭解什麼有效什麼無效。企圖避免設計上的失誤將會致使開發出來的產品質量劣質,由於沒有更多的經驗可用來借鑑,同時也會比進行一系列快速實驗更加費時。
約束是模型概念中很是重要的類別。它們一般是隱含的,將它們顯式地表現出來能夠極大地提升設計質量。
首先要說明的是,咱們都不但願過程變成模型的主要部分。對象是用來封裝過程的,這樣咱們只須要考慮對象的業務目的或意圖就能夠了。就像咱們以上用來安排貨運路線的運輸系統例子,安排路線的過程具備業務意義。SERVICE是顯示錶達這種過程的一種方式,同時它還會降異常複雜的算法封裝起來。
若是過程的執行有多種方式,那麼咱們也能夠用另外一種方法來處理它,那就是將算法自己或其中的關鍵部分放到一個單獨的對象中。這樣,選擇不一樣的過程就變成了選擇不一樣的對象,每一個對象都表示一種不一樣的STRATEGY(策略)。
那麼,過程是應該被顯示錶達出來,仍是應該被隱藏起來呢?區分的方法很簡單:它是常常被領域專家提起呢,仍是僅僅被看成計算機程序機制的一部分?
約束和過程是兩大類概念模型,當咱們用面嚮對象語言編程時,不會當即想到它們,然而它們一旦被咱們視爲模型元素,就真的可讓咱們的設計更爲清晰。
業務規則一般不適合做爲ENTITY和VALUE OBJECT的職責,並且規則的變化和組合也會被掩蓋領域對象的基本含義。可是將規則移出領域層的結果會更糟糕,由於這樣一來,領域代碼就再也不表達模型了。
邏輯編程提供了一個概念,即「謂詞」這種可分離、可組合的規則對象,可是要把這種概念用對象徹底實現是很麻煩的。同時,這種概念過於通用,在表達設計意圖方面,它的針對性不如專門的設計那麼好。
所以:爲特殊目的建立謂詞形式的顯式的VALUE OBJECT。SPECIFICATION就是一個謂詞,可用來肯定對象是否知足某些標準。
【學習心得】:項目的溝通成本之大,正是由於許多人的心裏都會有一顆「自尊心」,個人領域我最懂,個人技術牛逼,大家業務人員能夠一邊站。因此,想成爲一個合格的團隊成員,至少得讓本身能成爲一個合格的聆聽者。看似簡單,但我在生活中就發現了本身並不是一位很好的聆聽者。錯過了許多對本身有用的信息,更多仍是用了許多自覺得是的拍腦殼決策。
爲了使項目可以隨着開發工做的進行加速前進,而不會因爲它本身的老化將停滯不前,設計必需要讓人們樂於使用,並且易於作出修改。這就是柔性設計(supple design)。
不少過分設計(overengineering)藉着靈活性的名義而獲得合理的外衣。可是,過多的抽象層和間接設計經常成爲項目的絆腳石。看一下真正爲用戶帶來強大功能的軟件設計,你經常會發現一些簡單的東西。簡單並不容易作到。
若是開發人員爲了使用一個組件而必需要去研究它的實現,那麼就失去了封裝的價值。當某我的開發的對象或操做被別人使用時,若是使用這個組件的新的開發者不得不根據其實現來推測其用途,那麼他推測出來的可能並非那個操做或類的主要用途。若是這不是那個組件的用途,雖然代碼暫時能夠工做,但設計的概念基礎已經被誤用了,兩位開發人員的意圖也是背道而馳。
所以:在命名類和操做時要描述它們的效果和目的,而不是表露它們是經過何種方式達到目的的。這樣能夠使客戶開發人員沒必要去理解內部細節。這些名稱應該與UBIQUITOUS LANGUAGE保持一致,以便團隊成員能夠迅速推斷出它們的意義。在建立一個行爲以前先爲它編寫一個測試,這樣能夠促使你站在客戶開發人員的角度上來思考它。全部複雜的機制都應該封裝到抽象接口的後面,接口只代表意圖,而不代表方式。
大多數操做都會調用其餘的操做,然後者又會調用另一些操做。一旦造成這種任意深度的嵌套,就很難預測調用一個操做將要產生的全部後果。第二層和第三層操做的影響可能並非客戶開發人員有意爲之的,也是它們就變成了徹底意義上的反作用(任何對將來操做產生影響的系統狀態改變均可以成爲反作用)。
多個規則的相互做用或計算的組合產生的結果是很難預測的。開發人員在調用一個操做時,爲了預測操做的結果,必須理解它的實現以及它所調用的其餘方法的實現。若是開發人員不得不「揭開接口的面紗」,那麼接口的抽象做用就受到了限制。若是沒有了能夠安全地預見到結果的抽象,開發人員就必須限制「組合爆炸」,這就限制了系統行爲的豐富性。
所以:儘量把程序的邏輯放到函數(返回結果而不產生反作用的操做稱爲函數)中,由於函數是隻返回結果而不產生明顯反作用的操做。嚴格地把命令(引發明顯的狀態改變的方法)隔離到不返回領域信息的、很是簡單的操做中。當發現了一個很是適合承擔複雜邏輯職責的概念時,就能夠把這個複雜邏輯移到VALUE OBJECT中,這樣能夠進一步控制反作用。
把複雜的計算封裝到SIDE-EFFECT-FREE FUNCTION中能夠簡化問題,但實體仍然會留有一些有反作用的命令,使用這些ENTITY的人必須瞭解使用這些命令的後果。
若是操做的反作用僅僅是由它們的實現隱式定義的,那麼在一個具備大量相互調用關係的系統中,原由和結果會變得一團糟。理解程序的惟一方式就是沿着分支路徑來跟蹤程序的執行,封裝徹底失去了價值。跟蹤具體的執行也使抽象失去了意義。
所以:把操做的後置條件和類及AGGREGATE的固定規則表達清楚。若是在你的編程語言中不能直接編寫ASSERTION,那麼就把它們編寫成自動的單元測試。還能夠把它們寫到文檔或圖中(若是符合項目開發風格的話)。尋找在概念上內聚的模型,以便使開發人員更容易推斷出預期的ASSERTION,從而加快學習過程並避免代碼矛盾。
INTENTION-REVEALING INTERFACE清楚地代表了用途,SIDE-EFFECT-FREE FUNCTION和ASSERTION使咱們可以更準確地預測結果,所以封裝和抽象更加安全。
若是把模型或設計的全部元素都放在一個總體的大結構中,那麼它們的功能就會發生重複。外部接口沒法給出客戶可能關心的所有信息。因爲不一樣的概念被混合在一塊兒,它們的意義變得很難理解。
而另外一方面,把類和方法分解開也多是毫無心義的,這會使客戶更復雜,迫使客戶對象去理解各個細微部分是如何組合在一塊兒的。更糟的是,有的概念可能會徹底丟失。鈾原子的一半並非鈾。並且,粒度的大小並非惟一要考慮的問題,咱們還要考慮粒度是在哪一種場合下使用的。
所以:把設計元素(操做、接口、類和AGGREGATE)分解爲內聚單元,在這個過程當中,你對領域中一切重要劃分的直觀認識也要考慮在內。在連續的重構過程當中觀察發生變化和保證穩定的規律性,並尋找可以解釋這些變化模式的底層CONCEPTUAL CONTOUR。使模型與領域中那些一致的方面(正是這些方面使得領域成爲一個有用的知識體系)相匹配。
咱們的目標是獲得一組能夠在邏輯上組合起來的簡單接口,使咱們能夠用UBIQUITOUS LANGUAGE進行合理的表述,而且使那些無關的選項不會分散個人注意力,也不增長維護負擔。但這一般是經過重構才能獲得的結果,很難在前期就實現。並且若是僅僅是從技術角度進行重構,可能永遠也不會出現這種結果;只有經過重構獲得更深層次的理解,才能實現這樣的目標。
INTENTION-REVEALING INTERFACE使客戶可以把對象表示爲有意義的單元,而不只僅是一些機制。SIDE-EFFECT-FREE FUNCTION和ASSERTION使咱們能夠安全地使用這些單元,並對它們進行復雜的組合。CONCEPTUAL CONTOUR的出現使模型的各個部分變得更加穩定,也使得這些單元更直觀,更易於使用和組合。
即便是在MODULE內部,設計也會隨着依賴關係的增長而變得愈來愈難以理解。這加劇了咱們的思考負擔,從而限制了開發人員能處理的設計複雜度。隱式概念比顯式引用增長的負擔更大了。
低耦合是對象設計的一個基本要素。盡一切可能保持低耦合。把其餘全部無關概念提取到對象以外。這樣類就變得徹底獨立了,這就使得咱們能夠單獨地研究和理解它。每一個這樣的獨立類都極大地減輕了因理解MODULE而帶來的負擔。
盡力把最複雜的計算提取到STANDALONE CLASS(獨立的類)中,實現此目的的一種方法是從存在大量依賴的類中將VALUE OBJECT建模出來。低耦合是減小概念過載的最基本方法。獨立的類是低耦合的極致。
兩個實數相乘,結果仍爲實數(實數是全部有理數和全部無理數的集合)。因爲這一點永遠成立,所以咱們說實數的「乘法運算是閉合的」:乘法運算的結果永遠沒法脫離實數這個集合。當咱們對集合中的任意兩個元素組合時,結果仍在這個集合中,這就叫作閉合操做。
——The Math Forum,Drexel University
加法運算是實數集中的閉合運算。數學家們都極力避免去引入無關的概念,而閉合運算的性質正好爲他們提供了這樣一種方式。
所以:在適當狀況下,在定義操做時讓它的返回類型與其參數的類型相同。若是實現者(implementer)的狀態在計算中會被用到,那麼實現者實際上就是操做一個參數,所以參數和返回值應該與實現者有相同的類型。這樣的操做就是在該類型的實例集合中的閉合操做。閉合操做提供了一個高層接口,同時又不會引人對其餘概念的任何依賴。
【學習心得】:經歷了(還在經歷)一個近十年的項目,我想本身仍是比較有資格談談柔性設計的感覺。沒有學習這些柔性概念以前,咱們能持續高效並運行開發一個項目那麼長時間,功勞歸於一個重要的原則:簡單。起初,整個團隊都缺少以上這些實用的模式理論做爲參考,但你們都有秉承着一個「簡單」的共同原則,其實不知不覺中摸着石頭過河,在無數次重構中逐漸跟以上模式契合起來。固然,若是咱們能提早認識這些基礎的理論基礎知識,我想沒必要要的彎路會少走許多。也固然,系統還在不斷完善中,如今認識也不晚。
在《分析模式》一書中,Martin Fowler這樣定義分析模式:
分析模式是一種概念集合,用來表示業務建模中的常見結構。它可能只與一個領域有關,也可能跨多個領域。
Fowler所提出的分析模式來自於實踐經驗,所以只要用在合適的情形下,它們會很是實用。對於那些面對着具備挑戰性領域的人們,這些模型爲他們的迭代開發過程提供了一個很是有價值的起點。「分析模式」這個名字自己就強調了其概念本質。分析模式不是技術方案,他們只是參考,用來指導人們設計特定領域中的模型。
分析模式最大的做用是借鑑其餘項目的經驗,把那些項目中有關設計方向和實現結構的普遍討論與當前模型的理解結合起來。脫離具體的上下文來討論模型思想不但難以落地,並且還會形成分析與設計嚴重脫節的風險,而這一點正是MODEL-DRIVEN DESIGN堅定反對的。
當你能夠幸運地使用一種分析模式時,它通常並不會直接知足你的需求。但它爲你的研究提供了有價值的線索,並且提供了明確抽象的詞彙。它還能夠知道咱們的實現,從而省去不少麻煩。
咱們應該把全部分析模式的知識融入知識消化和重構的過程當中,從而造成更深入的理解,並促進開發。當咱們應用一種分析模式時,所獲得的結果一般與該模式的文獻中記載的形式很是想像,只是因具體狀況不一樣而略有差別。但有時徹底看不出這個結果與分析模式自己有關,然而這個結果仍然是受該模式思想的啓發而獲得的。
但有一個誤區是應該避免的。當使用衆所周知的分析模式中的術語時,必定要注意,無論其表面形式的變化有多大,都不要改變它所表示的基本概念。這樣作有兩個緣由,一是模式中蘊含的基本概念將幫助咱們避免問題,二是(也是更重要的緣由)使用被普遍理解或至少是被明確理解的術語能夠加強UBIQUITOUS LANGUAGE。若是在模型的天然演變過程當中模型的定義也發生改變,那麼就要修改模型名稱了。
【學習心得】:本章節主要仍是藉助《分析模式》一書中的例子,用實踐例子來分析系統是如何在演繹過程使用模型的。這種科學謹慎的作法,纔是一個工程師的基本觀念要求。
在《設計模式》中,有些(但並不是全部)模式可用做領域模式,但在這樣使用的時候,須要變換一下重點。有些模式反映了一些在領域中出現的深層概念。這些模式都有很大的利用價值。爲了在領域驅動設計中充分利用這些模式,咱們必須同時從兩個角度看待它們:從代碼的角度來看它們是技術設計模式,從模型的角度來看它們就是概念模式。
策略模式:定義了一組算法,將每一個算法封裝起來,並使它們能夠互換。STRATEGY容許算法獨立於使用它的客戶而變化[Gamma et al. 1995]
領域模型包含一些並不是用於解決技術問題的過程,將它們包含進來是由於它們處理問題領域具備實際的價值。當必須從多個過程當中進行選擇時,選擇的複雜性再加上多個過程自己的複雜性使局面失去控制。
所以:咱們須要把過程當中的易變部分提取到模型的一個單獨的「策略」對象中。將規則與它所控制的行爲區分開。按照STRATEGY設計模式來實現規則或可替換的過程。策略對象的多個版本表示了完成過程的不一樣方式。
組合模式:將對象組織爲樹來表示部分—總體的層次結構。利用COMPOSITE,客戶能夠對單獨的對象和對象組合進行一樣的處理。[Gamma et al.1995]
當嵌套容器的關聯性沒有在模型中反映出來時,公共行爲必然會在層次結構的每一層重複出現,並且嵌套也變得僵化(例如,容器一般不能包含同一層中的其餘容器,並且嵌套的層數也是固定的)。客戶必須經過不一樣的接口來處理層次結構中的不一樣層,儘管這些層在概念上可能沒有區別。經過層次結構來遞歸地收集信息也變得很是複雜。
所以:定義一個把COMPOSITE的全部成員都包含在內的抽象類型。在容器上實現那些查詢信息的方法時,這些方法返回由容器內容所彙總的信息。而「葉」節點則基於它們本身的值來實現這些方法。客戶只需使用抽象類型,而無需區分「葉」和容器。
【學習心得】:不少時候,技術人員釘子思惟是沒法區分技術角度和模型角度。雖然許多方法是相通的,但不通維度的思考方式也會產生巨大的效果。學以至用,不是停留在嘴巴上,是在實踐中證實的。
經過重構獲得更深層次的理解是一個涉及不少方面的過程。咱們有必要暫停一下,把一些要點概括到一塊兒。有三件事情是必需要關注的:
(1)以領域爲本;
(2)用一種不一樣的方式來看待事物;
(3)始終堅持與領域專家對話。
在尋求理解領域的過程當中,能夠發現更普遍的重構機會。但一提到傳統意義上的重構,咱們頭腦中就會出現這樣一幅場景:一兩位開發人員坐在鍵盤前面,發現一些代碼能夠改進,而後馬上動手修改代碼(固然還要用單元測試來驗證結果)。這個過程應該是一直進行下去,但它並非重構過程的所有。
與傳統重構觀點不一樣的是,即便在代碼看上去很整潔的時候也可能須要重構,緣由是模型的語言沒有與領域專家保持一致,或者新需求不能被天然地添加到模型中。重構的緣由也可能來自學習:當開發人員經過學習得到了更深入的理解,從而發現了一個獲得更清晰或更有用的模型的機會。
無論問題的根源是什麼,下一步都是要找到一種可以使模型表達變得更清楚和更天然的改進方案。這可能只須要作一些簡單、明顯的修改,只需幾小時便可完成。在這種狀況下,所作的修改相似於傳統重構。但尋找新模型可能須要更多時間,並且須要更多人蔘與。
修改的發起者會挑選幾位開發人員一塊兒工做,這些開發人員應該擅長思考該類問題,瞭解領域,或者掌握深厚的建模技巧。若是涉及一些難以捉摸的問題,他們還要請一位領域專家加入。想要保證重構迭代過程的效率,須要注意幾個關鍵事項:自主決定,注意範圍和休息,以及練習使用UBIQUITOUS LANGUAGE。
咱們沒有必要總去作一些無謂的重複工做。用於查找缺失概念或改進模型的頭腦風暴過程具備巨大的做用,經過這個過程能夠收集來自各個方面的想法,並把這些想法與已有知識結合起來。隨着知識消化的不斷開展,就能找到當前問題的答案。
軟件不只僅是爲用戶提供的,也是爲開發人員提供的。開發人員必須把他們編寫的代碼與系統的其餘部分集成到一塊兒。在迭代過程當中,開發人員反覆修改代碼。開發人員應該經過重構獲得更深層的理解,這樣既可以實現柔性設計,也可以從這樣一個設計中獲益。
若是一直等到徹底證實了修改的合理性以後纔去修改,那麼可能要等待太長時間了。項目正承受巨大的耗支,推遲修改將使修改變得更難執行,由於要修改的代碼已經變得更加複雜,並更深地嵌入到其餘代碼中。持續重構漸漸被認爲是一種「最佳實踐」,但大不部分團隊仍然對它抱有很大的戒心。
在探索領域的過程當中,在培訓開發人員的過程當中,以及在開發人員與領域專家進行思想交流的過程當中,必須始終堅持把「經過重構獲得更深層次理解」做爲這些工做的一部分。所以,當發生一下狀況時,就應該進行重構了:
□ 設計沒有表達出團隊對領域的最新理解;
□ 重要的概念被隱藏在設計中了(並且你已經發現了把它們呈現出來的方法);
□ 發現了一個能令某個重要的設計部分變得更靈活的機會。
傳統意義上的重構聽起來是一個很是穩定的過程。但經過重構獲得更深層理解每每不是這樣的。在對模型進行一段時間穩定的改進後,你可能忽然有所頓悟,而這會改變模型中的一切。這些突破不會天天都發生,然而很大一部分深層模型和柔性設計都來自這些突破。
這樣的狀況每每看起來不像是機遇,而更像危機。例如,你忽然發現模型中一些明顯的缺陷,在表達方面顯示出一個很大的漏洞,或存在一些沒有表達清楚的關鍵區域。或者有些描述是徹底錯誤的。這些都代表團隊對模型的理解已經達到了一個新的水平。他們如今站在更高的層次上發現了原有模型的弱點。他們能夠從這種角度構思一個更好的模型。
【學習心得】:我曾幾什麼時候一直認爲,發現本身問題是一種恥辱。這種思惟極其可怕,當我再也不發現本身問題的時候,那才叫可怕。在軟件領域,新思惟的提高叫重構,在生活方面,新觀念的造成叫重生。
模型最基本的要求是它應該保持內部一致,術語總具備相同的意義,而且不包含相互矛盾的規則:雖然咱們不多明確地考慮這些要求。模型的內部一致性又叫統一(unification),這種狀況下,每一個術語都不會有模棱兩可的意義,也不會有規則衝突。除非模型在邏輯上是一致的,不然它就沒有意義。在理想世界中,咱們能夠獲得涵蓋整個企業領域的單一模型。這個模型將是統一的,沒有任何相互矛盾或相互重疊的術語定義。每一個有關領域的邏輯聲明都是一致的。
但,大型系統開發並不是如此理想。在整個企業系統中保持這種水平的統一是一件得不償失的事情。在系統的各個不一樣部分中開發多個模型是頗有必要的,但咱們必須慎重地選擇系統的哪些部分能夠分開,以及它們之間是什麼關係。咱們須要用一些方法保持模型關鍵部分的高度統一。全部這些都不會自行發生,並且光有良好的意願也沒用的。它只有經過有意識的設計決策和創建特定過程才能實現。大型系統領域模型的徹底統一既不可行,也不划算。
細胞之因此可以存在,是由於細胞膜限定了什麼在細胞內,什麼在細胞外,而且肯定了什麼物質能夠經過細胞膜。
任何大型項目都會存在多個模型。而當基於不一樣模型的代碼被組合到一塊兒後,軟件就會出現bug,變得不可靠和難以理解。團隊成員之間的溝通變得混亂。人們每每弄不清楚一個模型不該該在哪一個上下文中使用。
所以:明確地定義模型所應用的上下文。根據團隊的組織,軟件系統的各個部分的用法以及物理表現(代碼和數據庫模式等)來設置模型的邊界。在這些邊界中嚴格保持模型的一致性,而不要收到邊界以外問題的干擾和混淆。
但記住,BUOUNDED CONTEXT不是MODULE。有時這兩個概念易引發混淆,但它們是具備不一樣動機的不一樣模式。確實,當兩組對象組成兩個不一樣模型時,人們幾乎老是把它們放在不一樣的MODULE中。這樣作的確提供了不一樣的命名空間(對不一樣的CONTEXT很重要)和一些劃分方法。但人們也會在一個模型中用MODULE來組織元素,它們不必定要表達劃分CONTEXT的意圖。MODULE在BOUNDED CONTEXT內部建立的獨立命名空間實際上令人們很難發現意外產生的模型分裂。
咱們經過定義這個BOUNDED CONTEXT,最終獲得了什麼?對CONTEXT內的團隊而言:清晰!對於CONTEXT以外的團隊而言:自由。固然,邊界只不過是一些特殊的位置。各個BUONDED CONTEXT之間的關係須要咱們仔細地處理。CONTEXTMAP(上下文圖)畫出了上下文範圍,並給出了CONTEXT以及它們之間聯繫的整體視圖,而幾種模式定義了CONTEXT之間的各類關係的性質。CONTINUOUS INTEGRATION(持續集成)的過程能夠使模型在BOUNDED CONTEXT中保持一致。
如何識別BOUNDED CONTEXT中的不一致?不少徵兆均可能代表模型出現了差別。最明顯的是已編碼的接口不匹配。對於更微妙的狀況,一些意外行爲也多是一種信號。採用了自動測試的CONTINUOUS INTEGRATION能夠幫助捕捉到這類問題,但語言上的混亂每每是一種早期信號。
將不一樣模型的元素組合到一塊兒可能會引起兩類問題:重複的概念和假同源。重複的概念是指兩個模型元素(以及伴隨的實現)實際上表示同一個概念,而假同源是指使用相同術語(或已實現的對象)的兩我的認爲他們是在談論同一件事情,但實際上並非這樣。假同源可能稍微少見一點,但它潛在的危害更大。
當不少人在同一個BOUNDED CONTEXT中工做時,模型很容易發生分裂。團隊越大,問題就越大,但即便三、4我的的團隊也有可能會遇到嚴重的問題。然而,若是將系統分解爲更小的CONTEXT,最終又難以保持集成度和一致性。
所以:創建一個把全部代碼和其餘實現工件頻繁地合併到一塊兒的過程,並經過自動化測試來快速查明模型的分類問題。嚴格堅持使用UBIQUITOUS LANGUAGE,以便在不一樣人的頭腦中演變出不一樣的概念時,是全部人對模型都能達成一個共識。
其餘團隊中的人員並非十分清楚CONTEXT的邊界,他們會不知不覺地作出一些更改,從而使邊界變得模糊或者使互連變得複雜。當不一樣的上下文必須互相鏈接時,它們可能會互相重疊。
所以:識別在項目中起做用的每一個模型,並定義其BOUNDED CONTEXT。這包括非面向對象子系統的隱含模型。爲每一個BOUNDED CONTEXT命名,並把名稱添加到UBIQUITOUS LANGUAGE中。描述模型之間的聯繫點,明確全部通訊須要的轉換,並突出任何共享的內容。先將當前的狀況描繪出來,之後再作改變。
CONTEXT MAP無需拘泥於任何特定的文檔格式。我發現相似本章的簡圖在可視化和溝通上下文圖方面頗有幫助。有些人可能喜歡使用較多的文本描述或別的圖形表示。在某些狀況下,團隊成員之間的討論就足夠了。需求不一樣,細節層次也不一樣。無論CONTEXT MAP採用什麼形式,它必須在全部項目人員之間共享,並被他們理解。它必須爲每一個BOUNDED CONTEXT提供一個明確的名稱,並且必須闡明聯繫點和它們的本質。
下面介紹的這些模式涵蓋了將兩個模型關聯起來的衆多策略。這些模式的主要區別包括你對另外一個模型的控制程度、兩個團隊以前合做水平和合做類型,以及特性和數據的集成程度。
當不一樣團隊開發一些緊密相關的應用程序時,若是團隊之間不進行協調,即便短期內可以得到快速進展,但他們開發出的產品可能沒法結合到一塊兒。租後可能不得不耗費大量精力在轉換層上,而且頻繁地進行改動,不如一開始就用CONTINUOUS INTEGRATION那麼省心省力,同時這也形成重複工做,而且沒法實現公共的UBIQUITOUS LANGUAGE所帶來的好處。
所以:從模型中選出兩個團隊都贊成共享的一個子集。固然,除了這個模型子集之外,還包括與該模型部分相關的代碼子集,或數據庫設計的子集。這部分明確共享的內容具備特殊的地位,一個團隊在沒與另外一個團隊商量的狀況下不該擅自更改它。功能系統要常常進行集成,但集成的頻率應該比團隊中CONTINUOUS INTEGRATION的頻率低一些。在進行這些集成的時候,兩個團隊都要運行測試。
咱們常常會碰到這樣的狀況:一個子系統主要服務於另一個子系統;「下游」組件執行分析或其餘功能,這些功能向「上游」組件反饋的信息很是少,全部依賴都是單向的。兩個子系統一般服務於徹底不一樣的用戶羣,其執行的任務也不一樣,在這種狀況下使用不一樣的模型會頗有幫助。
若是下游團隊對變動具備否決權,或請求變動的程序太複雜,那麼上游團隊的開發自由度就會受到限制。因爲擔憂破壞下游系統,上游團隊甚至會受到抑制。同時,因爲上游團隊掌握優先權。下游團隊有時也會無能爲力。
所以:在兩個團隊之間創建一種明確的客戶/供應商關係。在計劃會議中,下游團隊至關於上游團隊的客戶。根據下游團隊的需求來協商須要執行的任務併爲這些任務作預算,以便每一個人都知道雙方的約定和進度。兩個團隊共同開發自動化驗收測試,用來驗證預期的接口。把這些測試添加到上游團隊的測試套件中,以便做爲其持續集成的一部分來運行。這些測試使上游團隊在作出修改時沒必要擔憂對下游團隊產生反作用。
當兩個具備上游/下游關係的團隊不歸同一個管理者指揮時,CUSTOMER/SUPPLIER TEAM這樣的合做模式就不會湊效。勉強應用這種模式會給下游團隊帶來麻煩。
當兩個開發團隊具備上/下游關係時,若是上游團隊沒有動力來知足下游團隊的需求,那麼下游團隊將無能爲力。出於利他主義的考慮,上游開發人員可能會作出承諾,但他們可能不會履行承諾。下游團隊出於良好的意願會相信這些承諾,從而根據一些永遠不會實現的特性來制定計劃。下游項目只能被擱置,直到團隊最終學會利用現有條件自力更生爲止。下游團隊不會獲得根據他們的需求而量身定作的接口。
所以:經過嚴格聽從上游團隊的模型,能夠消除在BOUNDED CONTEXT之間進行轉換的複雜性。儘管這會限制下游設計人員的風格,並且可能不會獲得理想的應用程序模型,但選擇CONFORMITY模式能夠極大地簡化集成。此外,這樣還能夠與供應商團隊共享UBIQUITOUS LANGUAGE。供應商處於統治地位,所以最好使溝通變容易。他們從利他主義的角度出發,會與你分享信息。
SHARED KERNEL是兩個高度協調的團隊之間的合做模式,而CONFORMIST模式則是應對一個對合做不感興趣的團隊進行集成。
新系統幾乎老是須要與遺留系統或其餘系統進行集成,這些系統具備本身的模型。當把參與集成的BOUNDED CONTEXT設計完善而且團隊相互合做時,轉換層可能很簡單,甚至很優雅。可是,當邊界那側發生滲透時,轉換層就要承擔起更多的防禦職責。
當正在構建的新系統與另外一個系統的接口很大時,爲了克服鏈接兩個模型而帶來的困難,新模型所表達的意圖可能會被徹底改變,最終致使它被修改得像另外一個系統的模型了(以一種特定風格)。遺留系統的模型一般很弱。即便對於那些模型開發得很好的例外狀況,它們可能也不符合當前項目的須要。然而,集成遺留系統仍然具備很大的價值,並且有時仍是絕對必要的。
所以:建立一個隔離層,以便根據客戶本身的領域模型來爲客戶提供相關功能。這個層經過另外一個系統現有接口與其進行對話,而只需對那個系統做出不多的修改,甚至無需修改。在內部,這個層在兩個模型之間進行必要的雙向轉換。
這種鏈接兩個系統的機制可能會使咱們想到把數據從一個程序傳輸到另外一個程序,或者從一個服務器傳輸到另外一個服務器。咱們很快就會討論技術通訊機制的使用。但這些細節問題不該與ANTICORRUPTION LAYER混淆,由於ANTICORRUPTION LAYER並非向另外一個系統發送消息的機制。想反,它是不一樣的模型和協議之間轉換概念對象和操做的機制。
ANTICORRUPTION LAYER的公共接口一般以一組SERVICE形式出現,但偶爾也會採用ENTITY的形式。在咱們的模型中,把外部系統表示爲一個單獨組件多是沒有意義的。最好是使用多個SERVICE(或偶爾使用ENTITY),其中每一個SERVICE都使用咱們的模型來履行一致的職責。
對ANTICORRUPTION LAYER設計進行組織的一種方法是把它實現爲FACEDE、ADAPTER和轉換器的組合,外加兩個系統之間進行對話所需的通訊和傳輸機制。
咱們必須嚴格劃定需求的範圍。若是兩組功能之間的關係並不是必不可少,那麼兩者徹底能夠彼此獨立。由於集成老是代價高昂,而有時獲益卻很小。
所以:聲明一個與其餘上下文毫無關聯的BOUNDED CONTEXT,使開發人員可以在這個小範圍內找到簡單、專用的解決方案。
採用SEPARATE WAY(各行其道)模式須要預先決定一些選項。儘管持續重構最後能夠撤銷任何決策,但徹底隔離開發的模型是很難合併的。若是最終仍須要集成,那麼轉換層將是必要的,並且可能很複雜。固然,無論怎樣,這都是咱們將要面對的問題。如今,讓咱們回到更爲合做的關係上,來看一下幾種提升集成度的模式。
當一個子系統必須與大量其餘系統進行集成時,爲每一個集成都定製一個轉換層可能會減慢團隊的工做速度。須要維護的東西會愈來愈多。並且進行修改的時候擔憂的事情也會愈來愈多。
所以:定義一個協議,把你的子系統做爲一組SERVICE供其餘系統訪問。開放這個協議,以便全部須要與你的子系統集成的人均可以使用它。當有新的集成需求時,就加強並擴展這個協議,但個別團隊的特殊需求除外。知足這種特殊需求的方法是使用一次性的轉換器來擴充協議,以便使共享協議簡單且內聚。
這種通訊形式暗含一些共享的模型詞彙,它們是SERVICE接口的基礎。這樣,其餘子系統就變成了與OPEN HOST(開放主機)的模型相鏈接,而其餘團隊則必須學習HOST團隊所使用的專用術語。在一些狀況下,使用一個衆所周知的PUBLISHED LANGUAGE(公開發布的語言)做爲交換模型能夠減小耦合並簡化理解。
與現有領域模型進行直接的轉換可能不是一種好的解決方案。這些模型可能過於複雜或設計得較差。它們可能沒有被很好地文檔化。若是把其中一個模型做爲數據交互語言,它實際上就被固定住了,而沒法知足新的開發需求。
所以:把一個良好文檔化的、可以表達出所需領域信息的共享語言做爲公共的通訊媒介,必要時在其餘信息與該語言之間進行轉換。
講一個盲人摸象的故事:
第一個盲人碰巧摸到了大象寬闊結實的身軀,就覺得大象就像一堵牆;
······
第三個盲人碰巧把扭動着的象鼻抓在書中,就大膽認爲大象就像一條蛇;
第四個盲人急切伸出雙手,摸到了大象的膝蓋,就很明顯地認爲大象就像一顆樹;
······
第六個盲人一開始摸這頭大象,就抓住了它擺動着的尾巴,就認爲大象就像一根繩子。
即使他們對大象的本質不能達成完成的一致,這些盲人仍然能夠根據他們所觸摸到的大象身體的部位來擴展各自的認識。若是並不須要集成,那麼模型統不統一就可有可無。若是他們須要進行一些集成,那麼實際上並不須要對大象是什麼達成一致,而只要接受各類不一樣意見就會得到不少價值。這樣,他們就會在不知不覺中互不相讓。
當盲人想要分享更多大象的信息時,他們會共享單個BOUNDED CONTEXT獲得更大的價值。但統一多個模型幾乎老是意味着建立一個新模型:
通過一些想象和討論(也許是激烈的討論)以後,盲人們最終可能會認識到他們正在對一個更大總體的不一樣部分進行描述和建模。從不少方面來說,部分-總體的統一可能不須要花費不少工做。至少集成的第一步只需弄清楚各個部分是如何相連的就夠了。
儘管咱們已經把部分合併成一個總體,但獲得的模型仍是很簡陋的。他缺少內聚性,也沒有造成任何潛在的領域的輪廓。在持續精華的過程當中,新的理解可能會產生更深層的模型。
認可多個相互衝突的領域模型實際上正式面對現實的作法。經過明肯定義每一個模型都適用的上下文,能夠維護每一個模型的完整性,並清楚地看到要在兩個模型之間建立的任何特殊接口的含義。盲人沒辦法看到整個大象,但只要他們認可各自的理解是不完整的,他們的問題就能獲得解決。
在任什麼時候候,繪製出CONTEXT MAP來反映當前情況都是很重要的。可是,一旦繪製好CONTEXT MAP以後,你極可能想要改變現狀。現狀,你能夠開始有意識地選擇CONTEXT的邊界和關係。如下是一些指導原則:
按照自己價值來講,在決定是否擴展或分割BOUNDED CONTEXT時,應該權衡團隊獨立工做的價值以及能產生直接且豐富集成的價值,以這兩種價值的成本-效益做爲決策的依據。
在實踐中,團隊之間的行政關係每每決定了系統的集成方式。因爲彙報結構,有技術優點的統一可能沒法實現。管理層所要求的合併可能並不實用。你不會總能獲得你想要的東西,大你至少能夠評估出這些決策的代價,並反映給管理層,以便採起相應的措施來減小代價。
開發軟件項目時,咱們首先是對本身團隊正在開發的那些部分感興趣(「設計中的系統」),其次是對那些與咱們交互的系統感興趣。這是一種簡單、典型的狀況,能讓你對可能遇到的情形有一些粗略的瞭解。
實際上,咱們正式本身所處理的主要CONTEXT的一部分,這會在咱們的CONTEXT MAP中反映出來。只要咱們知道本身存在偏好,而且超過該CONTEXT MAP的應用邊界時可以意識到已越界,那麼就不會有什麼問題。
在畫出BOUNDED CONTEXT的邊界時,有無數種狀況,也有無數種選擇。但權衡時所要考慮的一般是下面所列出的某些因素。
首選較大的BOUNDED CONTEXT:
□ 當用一個統一模型來處理更多任務時,用戶任務之間的流動更順暢。
□ 一個內聚模型比兩個不一樣模型再加它們之間的映射更容易理解。
□ 兩個模型之間的轉換可能會很難(有時甚至是不可能的)。
□ 共享語言能夠使團隊溝通起來更清楚。
首選較小的BOUNDED CONTEXT:
□ 開發人員之間的溝通開銷減小了。
□ 因爲團隊和代碼規模較小,CONTINOUS INTEGRATION更容易了。
□ 較大的上下文要求更加通用的抽象模型,而掌握所需技巧的人員會出現短缺。
□ 不一樣模型能夠知足一些特殊需求,或者是可以把一些特殊用戶羣的專門術語和UBIQUITOUS LANGUAGE的專門術語包括進來。
最好從一些簡單的決策開始。一些子系統顯然不在開發中的系統的任何BOUNDED CONTEXT中。一些沒法當即淘汰的大型遺留系統和那些提供所需服務的外部系統就是這樣的例子。咱們很容易就能識別出這些系統,並把它們與你的設計隔離開。
在作出假設時必需要保持謹慎。咱們會輕易地認爲這些系統構成了其本身的BOUNDED CONTEXT,但大多數外部系統只是勉強知足定義。
這裏能夠應用3種模式。首先,能夠考慮SEPARATE WAY模式。固然,若是你不須要集成,就不用把它們包括進來。但必定要真正肯定不須要集成。只爲用戶提供對兩個系統的簡單訪問確實夠用嗎?集成要花費很大的代價並且還會分散精力,所以要儘量爲你的項目減輕負擔。
若是集成確實很是重要,能夠在兩種極端的模式之中選擇:CONFORMIST模式或ANTICORRUPTION LAYER模式。
你的項目團隊正在構建的軟件就是設計中的系統。你能夠在這個區域內聲明BOUNDED CONTEXT,並在每一個BOUNDED CONTEXT中應用CONTINOUS INTEGRATION,以便保持它們的統一。但應該有幾個上下文呢?各個上下文之間又應該是什麼關係呢?
狀況可能很是簡單:設計中的整個系統使用一個BOUNDED CONTEXT。例如,當一個少於10我的的團隊正在開發高度相關的功能時,這可能就是一個很好的選擇。
隨着團隊規模的增大,CONTINOUS INTEGRATION可能會變得困難起來(儘管我也曾看過一些較大的團隊仍能保持持續集成)。你可能但願採用SHARED KERNEL模式,並把幾組相對獨立的功能劃分到不一樣的BOUNDED CONTEXT中,使得在每一個BOUNDED CONTEXT中工做的人員少於10人。在這些BOUNDED CONTEXT中,若是有兩個上下文之間的全部依賴都是單向的,就能夠建成CUSTOMER/SUPPLIER DEVELOPMENT TEAM。
你可能認識到兩個團隊的思想大相徑庭,以至他們的建模工做老是發生矛盾。若是這種矛盾的緣由是你沒法改變或不想改變的,那麼可讓他們的模型採用SEPARATE WAY模式。在須要集成的地方,兩個團隊能夠共同開發維護一個轉換層,把它做爲惟一的CONTINOUS INTEGRATION點。這與同外部系統的集成正好相反,在外部集成中,通常由ANTICORRUPTION LAYER來起調節做用,並且從另外一端得不到太多的支持。
通常來講,每一個BOUNDED CONTEXT對應一個團隊。一個團隊也能夠維護多個BOUNDED CONTEXT,但多個團隊在一個上下文中工做倒是比較難的。
你可能決定經過不一樣的BOUNDED CONTEXT來知足這些特殊需求,除了轉換層的CONTINOUS INTEGTATION之外,讓模型採用SEPARATE WAY模式。UBIQUITOUS LANGUAGE的不一樣專用術語將圍繞這些模型以及它們所基於的行話來發展。若是兩種專用術語有不少重疊之處,那麼SHARED KERNEL模式就能夠知足特殊化要求,同時又能把轉換成本減至最小。
最重要的是:這個用戶羣的專門術語有多大的價值?你必須在團隊獨立操做的價值與轉換的風險之間作出權衡,而且留心合理地處理一些沒有價值的術語變化。但記住,在須要大量集成的地方,轉換成本會大大增長。
在複雜系統中,對打包和部署進行協調是一項繁瑣的任務,這類任務老是要比看上去可貴多。BOUNDED CONTEXT策略的選擇將影響部署。因爲部署環境和技術存在不一樣,有不少技術因素須要考慮。但BOUNDED CONTEXT關係能夠爲咱們指出重點問題。轉換接口已經被標出。因此,繪製CONTEXT邊界時應該反映出部署計劃的可行性。
經過總結這些知道原則可知有不少統一或集成模型的策略。通常來講,咱們須要在無縫功能集成的益處和額外的協調和溝通工做之間作出權衡。
不少狀況下,咱們不是從頭開發一個項目,而是會改進一個正在開發的項目。在這種狀況下,第一步是根據當前的情況來定義BOUNDED CONTEXT。這很關鍵。爲了有效地定義上下文,CONTEXT MAP必須反映出團隊的實際工做,而不是反映那個經過遵照以上描述的指導原則而得出的理想組織。
描述了當前真實的BOUNDED CONTEXT以及它們的關係之後,下一步就是圍繞當前組織結構來增強團隊的工做。在CONTEXT中增強CONTINOUS INTEGRATION。把全部分散的轉換代碼重構到ANTICORRUPTION LAYER中。命名現有的BOUNDED CONTEXT,並確保它們處於項目的UBIQUITOUS LANGUAGE中。
下一節將討論如何修改CONTEXT邊界:轉換。
像建模和設計的其餘方面,有關BOUNDED CONTEXT的決策並不是不可改變的。在不少狀況下,咱們必須改變最初有關邊界以及BOUNDED CONTEXT之間關係的決策,這是不可避免的。通常而言,分割CONTEXT是很容易,但合併它們或改變它們之間的關係卻很難。下面將介紹幾種有表明性的修改,它們很難,但也很重要。
合併BOUNDED CONTEXT的動機不少:翻譯開銷夠高、重複現象很明顯。合併很難,但何時作都不晚,只是須要一些耐心。
即便你的最終目標是徹底合併一個採用CONTINUOS INTEGRATION的CONTEXT,也應該先過渡到SHARED KERNEL。
(1)評估現狀。在開始統一兩個CONTEXT以前,必定要確信它們確實須要統一。
(2)創建合併過程。你須要決定代碼的共享方式以及模塊應該採用哪一種命名約定。SHARED KERNEL的代碼至少每週要集成一次,並且它必須有一個測試套件。在開發任何共享代碼以前,先把它設置好。(測試套件將是空的,所以很容易經過!)
(3)選擇某個小的子領域做爲開始,它應該是兩個CONTEXT中重複出現的子領域,但不是CORE DOMAIN的一部分。
(4)從兩個團隊中共選出2~4位開發人員組成一個小組,有他們來爲子領域開發一個共享的模型。
(5)來自兩個團隊的開發成員一塊兒負責實現模型(或修改要共享的現有代碼)、肯定各類細節並使模型開始工做。若是這些開發人員在模型中遇到了問題,就從第(3)步開始從新組織團隊,並進行必要的概念修訂工做。
(6)每一個團隊的開發人員都承擔與新的SHARED KERNEL集成的任務。
(7)清除那些再也不須要的翻譯。
若是你的KERNEL正在擴大,你可能會被徹底統一兩個BOUNDED CONTEXT的優勢所吸引。但這並不僅是一個解決模型差別的問題。你將改變團隊的結構,並且最終會改變人們所使用的語言。這個過程從人員和團隊開始準備。
(1)確保每一個團隊都已經創建了CONTINOUS INTEGRATION所需的全部過程(共享代碼全部權、頻繁集成等)。兩個團隊協商集成步驟,以便全部人都以同一步調工做。
(2)團隊成員在團隊之間流動。這樣能夠造成一大批同時理解兩個模型的人員,而且能夠把兩個團隊的人員聯繫起來。
(3)澄清每一個模型的精髓。
(4)如今,團隊應該有了足夠的信心把核心領域合併到SHARED KERNEL中。
(5)隨着SHARED KERNEL的增加,把集成頻率提升到天天一次,最後實現CONTINOUS INTEGRATION。
(6)當SHARED KERNEL逐漸把先前兩個BOUNDED CONTEXT的全部內容都包括進來的時候,你會發現要麼造成了一個大的團隊,要麼造成了兩個較小的團隊,這兩個較小的團隊共享一個CONTINOUS INTEGRATION的代碼庫,並且團隊成員能夠常常在兩個團隊之間來回流動。
好花美麗不常開,好景怡人不常在,就算遺留計算機軟件也同樣會走向終結。但這可不會自動自發地出現。這些老的系統可能與業務及其餘系統緊密交織在一塊兒,所以淘汰它們可能須要不少年。好在咱們並不須要一次就把全部東西都淘汰掉。
首先要執行的步驟是肯定測試策略。應該爲新系統中的新功能編寫自動的單元測試,但逐步淘汰遺留系統還有一些特殊的測試要求。一些組織會在某段時間內同時運行新舊兩個系統。在任何一次迭代中:
(1)肯定遺留系統的哪一個功能能夠在一個迭代中被添加到某個新系統中;
(2)肯定須要在ANTICORRUPTION LAYER中添加功能;
(3)實現;
(4)部署;
(5)找出ANTICORRUPTION LAYER中那些沒必要要的部分,並去掉它們;
(6)考慮刪除遺留系統中目前未被使用的模塊,雖然這種作法未必實際。
不斷重複這幾個步驟。遺留系統應該愈來愈少地參與業務,最終,替換工做會看到但願的曙光並徹底中止遺留系統。
咱們已經經過一系列特意的協議與其餘系統進行了集成,但隨着須要訪問的系統逐漸增多,維護負擔也不斷增長,或者交互變得很難理解。咱們須要經過PUBLISHED LANGUAGE來規範系統之間的關係。
(1)若是有一種行業標準語言可用,則儘量評估並使用它。
(2)若是沒有標準語言或預先公開發布的語言,則完善做爲HOST的系統的CORE DOMAIN。
(3)使用CORE DOMAIN做爲交換語言的基礎,儘量使用像XML這樣的標準交互範式。
(4)(至少)向全部參與協做的各方發佈語言。
(5)若是涉及新的系統架構,那麼也要發佈它。
(6)爲每一個協做系統構建轉換層。
(7)切換。
如今,當加入更多協做系統時,對整個系統的破壞已經減至最小了。
【學習心得】:學以至用,具體問題具體分析。模式畢竟是巨人的肩膀,要學會站着巨人肩膀看事情,不管項目多大仍是多下,又或者團隊多大仍是多小,總有屬於當前本身的模式。結合自身狀況,找準定位。咱們所作的大部分事情幾乎都有方法或模式借鑑,千萬不要埋頭單幹。就像耗子叔所說,學會Evidence Driven:任何討論和分析都要基於權威的證據、數據或是引用。在咱們作設計的時候,或是有爭論的時候,說服對方最好的方式就是拿出證據、數據或是權威引用。好比:個人XX設計參考了TCP協議中的XX設計,個人XX觀點是基於XX開源軟件的實現……若是爭論不休就中止爭論,而後各自收集和調查本身觀點的佐證。
如何才能專一於核心問題而不被大量的次要問題淹沒呢?LAYERED ARCHITECTURE能夠把領域概念從技術邏輯中(技術邏輯確保了計算機系統可以運轉)分離出來,但在大型系統中,即便領域被分離出來,它的複雜性也可能仍然難以管理。
精煉是把一堆混雜在一塊兒的組件分開的過程,以便經過某種形式從中提取出最重要的內容,而這種形式將使它更有價值,也更有用。模型就是知識的精煉。經過每次重構所獲得的更深層的理解,咱們得以把關鍵的領域知識和優先級提取出來。
本章將展現對CORE DOMAIN進行戰略精煉的系統性方法,解釋如何在團隊中有效地統一認識,並提供一種用於討論工做的語言。
在設計大型系統時,有很是多的組成部分——它們都很複雜並且對開發的功能也相當重要,到致使真正的業務資產——領域模型最爲精華的部分——被掩蓋和忽略了。
一個嚴峻的現實是咱們不可能對全部設計部分進行同等的精化,而是必須分出優先級。爲了使領域模型成爲有價值的資產,必須整齊地梳理出模型的真正核心,並徹底根據這個核心來建立應用程序的功能。但原本就稀缺的高水平開發人員每每會把工做重點放在技術基礎設施上,或者只是去解決那些不須要專門領域知識就能理解的領域問題(這些問題都已經有了很好的定義)。
所以:對模型進行提煉。找到CORE DOMAIN並提供一種易於區分的方法把它與那些去輔助做用的模型和代碼分開。最有價值和最專業的概念要輪廓分明。儘可能壓縮CORE DOMAIN。讓最有才能的人來開發CORE DOMAIN,並據此要求進行相應的招聘。在CORE DOMAIN中努力開發可以確保現實系統藍圖的深層模型和柔性設計。仔細判斷任何其餘部分的投入,看它是否可以支持這個提煉出來的CORE。
咱們須要關注的是那些可以表示業務領域並解決業務問題的模型部分。一個應用程序中的CORE DOMAIN在另外一個應用程序中可能只是通用的支持組件。儘管如此,仍然能夠在一個項目中(並且一般在一個公司中)定義一個一致的CORE。像其餘設計部分同樣,人們對CORE DOMAIN的認識也會隨着迭代而發展。開始時,一些特殊關係可能顯得不重要。而最初被認爲是核心對象可能逐漸被證實只是起支持做用。
在項目團隊中,技術能力最強的人員每每缺少豐富的領域知識。這限制了他們的做用,而且更傾向於分派他們來開發一些支持組件,從而造成了一個惡性循環——知識的缺少使他們遠離了那些可以學到領域知識的工做。
打破這種噁心循環是很重要的,方法是創建一支由開發人員和一位或多位領域專家組成的聯合團隊,其中開發人員必須能力很強、可以長期穩定地工做而且學習領域知識很是感興趣,而領域專家則要掌握深厚的業務知識。若是你認真對待領域設計,那麼它就是一項有趣且充滿技術挑戰的工做。
本章接下來將要介紹各類精煉技術,它們在使用順序上基本沒什麼要求,但對設計的改動卻大不相同。請往下看:
模型中有些部分除了增長複雜性之外並無捕捉或傳遞任何專門的知識。任何外來因素都會是CORE DOMAIN愈發的難以分辨和理解。模型中充斥着大量衆所周知的通常原則,或者專門的細節,這些細節並非咱們的主要關注點,而只是起到支持做用。然而,不管它們是多麼通用的元素,它們對實現系統功能和充分表達模型都是極爲重要的。
所以:識別出那些與項目意圖無關的內聚子領域。把這些子領域的通用模型提取出來,並放到單獨的MODULE中。任何專有的東西都不該該放在這些模塊中。把它們分離出來之後,在繼續開發的過程當中,它們的優先級應低於CORE DOMAIN的優先級,而且不要分派核心開發人員來完成這些任務(由於他們不多可以從這些任務中得到領域知識)。此外,還能夠考慮爲這些GENERIC SUBDOMAIN使用現成的解決方案或「公開發布的模型」(PUBLISHED MODEL)。
當開發這樣的軟件包時,有如下幾種選擇:
一、現成的解決方案
二、公開發布的設計或模型
三、把實現外包出去
四、內部實現
在項目開始時,模型一般並不存在,可是模型開發的需求是早就肯定下來的重點。在後面的開發階段,咱們須要解釋清楚系統的價值,但這並不須要深刻地分析模型。此外,領域模型的關鍵方面可能跨越多個BOUNDED CONTEXT,並且從定義上看,沒法將這些彼此不一樣的模型組織起來代表其共同的關注點。
所以:寫一份CORE DOMAIN的簡短描述(大約一頁紙)以及它將會創造的價值,也就是「價值主張」。那些不能將你的領域模型與其餘領域模型區分開的方面就不要寫了。展現出領域模型是如何實現和均衡各方面利益的。這份描述要儘可能精簡。儘早把它寫出來,隨着新的理解隨時修改它。
DOMAIN VISION STATEMENT能夠用做一個指南,它幫助開發團隊在精煉模型和代碼的過程當中保持統一的方向。團隊中的非技術成員,管理層甚至是客戶也均可以共享領域願景說明。
DOMAIN VISION STATEMENT從寬泛的角度對CORE DOMAIN進行了說明,但它把什麼是具體核心模型元素留給人們本身去解釋和猜想。除
非團隊的溝通極其充分,不然單靠VISION STATEMENT是很難產生什麼效果的。
儘管團隊成員可能大致上知道核心領域是由什麼構成的,但CORE DOMIAN中到底包含哪些元素,不一樣的人會有不一樣的理解,甚至同一我的在不一樣的時間也有會不一樣的理解。若是咱們老是要不斷過濾模型以便識別出關鍵部分,那麼就會分散本應該投入到設計上的精力,並且這還須要普遍的模型知識。所以,CORE DOMAIN必需要很容易被分辨出來。
對代碼所作的重大結構性改動是識別CORE DOMAIN的理想方式,但這些改動每每沒法在短時間內完成。事實上,若是團隊的認識還不夠全面,這樣的重大代碼修改是很難進行的。
經過修改模型的組織結構(如劃分GENERIC SUBDOMIAN和本章後面要介紹的一些改動),能夠用MODULE表達出核心領域。但若是把它做爲表達CORE DOMAIN的惟一方法,那麼對模型的改動會很大,所以很難立刻看到結果。
咱們可能須要用一種輕量級的解決方案來補充這些激進的技術手段。可能有一些約束使你沒法從物理上分離出CORE,或者你多是從已有代碼開始工做的,而這些代碼並無很好地區分出CORE,但你確實很須要知道什麼是CORE並創建共識,以便有效地經過重構進行更好的精煉。咱們能夠經過如下兩種典型的表明性技術來突出核心:
編寫一個很是簡短的文檔(3~7頁,每頁內容沒必要太多),用於描述CORE DOMAIN以及CORE元素之間的主要交互過程。但獨立文檔帶來的全部常見風險也會在這裏出現(以下所示),控制這些風險的最好方法是保持絕對的精簡。
(1)文檔可能得不到維護;
(2)文檔可能沒人閱讀;
(3)因爲多個信息來源,文檔可能達不到簡化複雜性的目的。
可能你會遇到一份數百頁的「領域模型」文檔等資料,但無需慌張。把模型的主要存儲庫中的CORE DOMAIN標記出來,不用特地去闡明其角色。是開發人員很容易就知道什麼在覈心內,什麼在覈心外。只需作不多的處理和維護工做,便可讓處理模型的人員很清晰地看到CORE DOMAIN了。
若是精煉文檔歸納了CORE DOMAIN的核心元素,那麼它就能夠做爲一個指示器——用以指示模型改變的重要程度。當模型或代碼的修改影響到精煉文檔時,須要與團隊其餘成員一塊兒協商。當對精煉文檔作出修改時,須要當即通知全部團隊成員,並且要把心版本的文檔分發給他們。CORE外部的修改或精煉文檔外部的細節修改則無需協商或通知,能夠直接把它們集成到系統中,其餘成員在後續工做過程當中天然會看到這些修改。這樣開發人員就擁有了XP所建議的徹底的自治性。
計算有時會很是複雜,使設計開始變得膨脹。機械性的「如何作」大量增長,把概念性的「作什麼」徹底掩蓋了。爲了解決問題提供算法的大量方法掩蓋了那些用於表達問題的方法。
所以:把概念上的COHESIVE MECHANISM(內聚機制)分離到一個單獨的輕量級框架中。要特別注意公式或那些有完備文檔的算法。用一個INTENTION-REVEALING INTERFACE來暴露這個框架的功能。如今,領域中的其餘元素就能夠只專一於如何表達問題(作什麼)了,而把解決方案的複雜細節(如何作)轉移給了框架。
GENERIC SUBDOMIAN與COHESIVE MECHANISM的動機是相同的——都是爲CORE DOMAIN減負。區別在於兩者所承擔的職責的性質不一樣。GENERIC SUBDOMAIN是以描述性的模型做爲基礎的,它用這個模型表示出團隊會如何看待領域的某個方面。在這一點上與CORE DOMIAN沒什麼區別,只是重要性和專門程度較低而已。COHESIVE MECHANISM並不表示領域,它的目的是解決描述性模型所提出來的一些複雜的計算問題。模型提出問題,COHESIVE MECHANISM解決問題。因此GENERIC SUBDOMAIN是模型級別維度,而COHESIVE MECHANISM是CORE DOMAIN的一部分。
聲明式設計是一種精簡的設計風格,在本書中也多處說起。精煉的價值在於使你可以看到本身正在作什麼,不讓無關細節分散你的注意力,並經過不斷削減獲得核心。若是領域中那些起到支持做用的部分提供了一種簡練的語言,可用於表示CORE的概念和規則,同時又可以把計算或實施這些概念和規則的方式封裝起來,那麼CORE DOMAIN的重要部分就能夠採用聲明式設計。
COHESIVE MACHANISM用途最大的地方是它經過設計一個INTENTION-REVEALING INTERFACE來提供訪問,而且具備概念上一致的ASSERTION和SIDE-EFFECT-FREE FUNCTION。利用這些MECHANISM和柔性設計,CORE DOMAIN能夠使用有意義的聲明,而沒必要調用難懂的函數。但最不一樣尋常的回報來自於使CORE DOMAIN的一部分產生突破,獲得一個深層模型。
把GENERIC SUBDOMAIN提取出來能夠減小混亂,而COHESIVE MECHANISM能夠把複雜操做封裝起來。這樣能夠獲得一個更專一的模型,從而減小了那些對用戶活動沒什麼價值、分散注意力的方面。但咱們不太可能爲領域模型中全部非CORE元素安排一個適當的去處。SEGREGATED CORE(分離的核心)採用直接的方法從結構上把CORE DOMAIN劃分出來。
模型中的元素可能有一部分屬於CORE DOMAIN,而另外一部分起支持做用。核心元素可能與通常元素緊密耦合在一塊兒。CORE的概念內聚性可能不很強,看上去也不明顯。這種混亂性和耦合關係抑制了CORE。設計人員若是沒法清晰地看到最重要的關係,就會開發出脆弱的設計。
經過把GENERIC SUBDOMAIN提取出來,能夠從領域中清除一些干擾性的細節,使CORE變得更清楚。但識別和澄清全部這些子領域是很困難的工做,並且有些工做看起來並不值得去作。同事,最重要的CORE DOMAIN仍然與剩下的那些元素糾纏在一塊兒。
所以:對模型進行重構,把核心概念從支持性元素(包括定義得不清楚的那些元素)中分離出來,並加強CORE的內聚性,同時減小它與其餘代碼的耦合。把全部通用元素或支持性元素提取出來到其餘對象中,並把這些對象放到其餘的包中——即便這會把一些緊密耦合的元素分開。
這裏基本上採用了與GENERIC SUBDOMAIN同樣的原則,只是從另外一個方向考慮而已。就目前來看,使用哪一種簡單解決方案均可以,只需把注意力集中在SEGREGATED CORE(分離的核心)上便可。
一般,即使是CORE DOMAIN模型也會包含太多的細節,以致於它很難表達出總體視圖。當不一樣MODULE的子領域之間有大量交互時,要麼須要在MODULE之間建立不少引用,這在很大程度上抵消了劃分模塊的價值;要麼就必須間接地實現這些交互,然後者會使模型變得晦澀難度。
所以:把模型中最基本的概念識別出來,並分離到不一樣的類、抽象類或接口中。設計這個抽象模型,使之可以表達重要組件之間的大部分交互。把這個完整的抽象模型放到它本身的MODULE中,而專用的、詳細的實現類則留在由子領域定義的MODULE中。
【學習心得】:我很幸運,能遇到一個近作了近十年的大型應用項目,我記得從剛開始只有兩臺刀片機服務發展至目前百來臺高配置級別的PC規模。雖然我如今纔看到這本書,但這十年的摸索過程其實就是這章節的實現。實在是很是寶貴的經驗。
在一個大的系統中,若是由於缺乏一種全局性的原則而令人們沒法根據元素在模式(這些模式被應用於整個設計)中的角色來解釋這些元素,那麼開發人員就會陷入「只見樹木,不見森林」的境地。
「大型結構」是一種語言,人們能夠用它來從大局上討論和理解系統。
設計一種應用於整個系統的規則(或角色和關係)模式,令人們能夠經過它在必定程度上了解各個部分在總體中所處的位置(即便是在不知道各個部分的詳細職責的狀況下)。本章將探討一些能成功構建這種設計結構的模式。
一個沒有任何規則的隨意設計會產生一些沒法理解總體含義且很難維護的系統。但架構中早期的設計假設又會使項目變得一籌莫展,並且會極大地限制應用程序中某些部分的開發人員/設計人員的能力。很快,開發人員就會爲適應結構而不得不在應用程序的開發上委曲求全,要麼就是徹底推翻架構而又回到沒有協調的開發老路上來。
所以:讓這種概念上的大型結構隨着應用程序一塊兒演變,甚至能夠變成一種徹底不一樣結構的風格。不要依次過度限制詳細的設計和模型決策,這些決策和模型決策必須在掌握了詳細以後才能肯定。
於CONTEXT MAP不一樣的是,大型結構是可選的。當發現一種大型結構能夠明顯使系統變得更加清晰,而又沒有對模型開發施加一些不天然的約束時,就應該採用這種結構。使用不合適的結構還不如不使用它,所以最好不要爲了追求設計的完整性而勉強去使用一種結構,而應該找到儘量精簡的方式解決所出現問題。要記住寧缺毋濫的原則。
軟件設計每每很是抽象且難於掌握。開發人員和用戶都須要一些切實可行的方式來理解系統,並共享系統的一個總體視圖。
所以:當系統的一個具體類比正好符合團隊成員對系統的想象,而且可以引導他們向着一個有用的方向進行思考時,就應該把這個類比用做一種大型結構。圍繞這個隱喻來組織設計,並把它吸取到UBIQUITOUS LANGUAGE中。SYSTEM METAPHOR應該既能促進系統的交流,又能指導系統的開發。它能夠增長系統不一樣部分之間的一致性,甚至能夠跨越不一樣的BOUNDED CONTEXT。但全部隱喻都不是徹底精確的,所以應不斷檢查隱喻是否過分或不恰當,當發現它起到妨礙做用時,要隨時準備放棄它。
SYSTEM METAPHOR並不適用於全部項目。從整體上講,大型結構不是必需要用的。在極限編程的12個實踐中,SYSTEM METAPHOR的角色能夠由UBIQUITOUS LANGUAGE來承擔。當項目中發現一種很是適合的SYSTEM METAPHOR或其餘大型結構時,應該用它來補充UBIQUITOUS LANGUAGE。
若是每一個對象的職責都是人爲分配的,將沒有統一的指導原則和一致性,也沒法把領域做爲一個總體來處理。爲了保持大型模型的一致,有必要在職責分配上實施必定的結構化控制。
所以:注意觀察模型中的概念依賴性,以及領域中不一樣部分的變化頻率和變化的緣由。若是在領域中發現了天然的層次結構,就把它們轉換爲寬泛的抽象職責。這些職責應該描述系統的高層目的和設計。對模型進行重構,使得每一個領域對象,AGGREGATE和MODULE的職責都清晰地位於一個職責層當中。
想要找到一種適當的RESPONSIBILITY LAYER或大比例結構,須要理解問題領域並反覆進行實驗。若是遵循EVOLVING ORDER,那麼最初的起點並非十分重要,儘管差勁的選擇確實會加大工做量。結構可能最後演變得面目全非。所以,下面將給出一些指導方針,不管是剛開始選擇一種結構,仍是對已有結構進行轉換,這些指導方針都適用。
當對層進行刪除、合併、拆分和從新定義等操做時,應尋找並保留一下一些有用的特徵:
□ 場景描述。層應該可以表達出領域的基本實現或優先級選擇一種大比例結構與其說是一種技術決策,不如說是一種業務建模決策。
□ 概念依賴性。「較高」層概念的意義應該依賴「較低」層,而低層概念的意義應該獨立於較高層。
□ CONCEPTUAL CONTOUR。若是不一樣層的對象必須具備不一樣的變化頻率或緣由,那麼層應該可以允許它們之間的變化。
□ 潛能層。咱們可以作什麼?潛能層不關心咱們打算作什麼,而關心可以作什麼。如企業的資源(包括人力資源)以及這些資源的組織方式是潛能層的核心。
□ 做業層。咱們正在作什麼?咱們利用這些潛能作了什麼事情?像潛能層同樣,這個層也應該反映出現實狀況,而不是咱們設想的情況。如咱們但願在這個層中看到本身的工做和活動:咱們正在銷售什麼,而不是可以銷售什麼。一般來講,做業層對象能夠引用潛能層對象,它甚至能夠由潛能層對象組成,但潛能層對象不該該引用做業層對象。
□ 決策支持層。應該採起什麼行動或制定什麼策略?這個層是用來做出分析和制定決策的。它根據來自較低層(如潛能層或做業層)的信息進行分析。決策支持軟件能夠利用歷史信息來主動尋找適用於當前和將來做業的機會
□ 策略層。規則和目標是什麼?規則和目標主要是被動的,但它們約束着其餘層的行爲。這些交互的設計是一個微妙的問題。有時策略會做爲一個參數傳遞給較低層的方法。有時會使用STRATEGY模式。策略層與決策支持層可以進行很好的協做,決策支持層提供了用於搜索策略層所設定的目標的方式,這些目標又受到策略層設定的規則約束。
□ 承諾層。咱們承諾了什麼?這個層具備策略層的性質,由於他表述了一些指導將來運營的目標;但它也有做業層的性質,由於承諾是做爲後續由於活動的一部分而出現和變化的。
雖然這5個層對不少企業系統都適用,但並非全部領域的主要概念都涵蓋在這5個層中。有些狀況下,在設計中生硬地套用這種形式反而會起副作用,而使用一組更天然的RESPONSIBILITY LAYER會更有效。
咱們須要對分層結構進行調整和實驗,但必定要使分層系統保持簡單,若是層數超過4或5,就比較難處理了。層數越多將沒法有效地描述領域,並且原本要使用大比例結構解決的複雜性問題又會以一種新的方式出現。咱們必須對大比例結構進行嚴格的精簡。
若是一個領域與上述討論毫無關係,全部的分層可能都必須從頭開始。最後,咱們必須根據直覺選擇一個起點,而後經過EVOLVING ORDER來改進它。
若是在一個應用程序中,ENTITY的角色和它們之間的關係在不一樣的狀況下有很大變化,那麼複雜性會顯著增長。在這種狀況下,不管是通常的模型仍是高度定製的模型,都沒法知足用戶的需求。爲了兼顧各類不一樣的情形,對象須要引用其餘的類型,或者須要具有一些在不一樣狀況下包括不一樣使用方式的屬性。具備相同數據和行爲的類可能會大量增長,而這些類的惟一做用只是爲了知足不一樣的組裝規則。
所以:建立一組不一樣的對象,用它們來描述和約束基本模型的結構和行爲。把這些對象分爲兩個「級別」,一個是很是具體的級別,另外一個級別則提供了一些可供用戶或超級用戶定製的規則和知識。
若是獲得合理的運用,KNOWLEDGE LEVEL可以解決一些其餘方式很難解決的問題。若是系統中某些部分的定製很是關鍵,而要是不提供定製能力就會破壞掉整個設計,這時就能夠利用知識級別來解決這一問題。
像其餘大比例結構同樣,KNOWLEDGE LEVEL也不是必需要使用的。沒有它,對象照樣能工做,並且團隊可能仍然可以認識到他們須要將Employee與Payroll分離。當項目進行到某個時刻,這種結構看起來已經沒什麼用了,那麼就能夠放棄它。
咋看上去,KNOWLEDGE LEVEL像是RESPONSIBILITY LAYER(特別是策略層)的一個特例,但它並非。首先,KNOWLEDGE LEVEL兩個級別之間的依賴是雙向的,而RESPONSIBILITY LAYER在層次結構中,較低的層不依賴於較高的層。實際上,RESPONSIBILITY LAYER能夠與其餘大部分的大比例結構共存,它提供了另外一種用來組織模型的維度。
在深刻理解和反覆精煉基礎上獲得的成熟模型中,會出現不少機會。一般只有在同一個領域中實現了多個應用程序以後,纔有機會使用PLUGGABLE COMPONENT FRAMEWORK(可插入式組件框架)。
當不少應用程序須要進行相互操做時,若是應用程序都基於相同的一些抽象,但它們是獨立設計的,那麼在多個BOUNDED CONTEXT之間的轉換會限制它們的集成。各個團隊之間若是不能緊密地協做,就沒法造成一個SHARED KERNEL。重複和分裂將會增長開發和安裝的成本,並且互操做會變得很難實現。
所以:從接口和交互中提煉一個ABSTRACT CORE,並建立一個框架,這個框架要容許這些接口各類不一樣實現被自由替換。一樣,不管是什麼應用程序,只要它嚴格地經過ABSTRACT CORE的接口進行操做,那麼就能夠容許它使用這些組件。
PLUGGABLE COMPONENT FRAMEWORK也有幾個缺點:
□ 一個缺點是它是一種很是難以使用的模式。它須要高度精確的接口設計和一個很是深刻的模型,以便把一些必要的行爲捕獲到ABSTRACT CORE中。
□ 另外一個很大的缺點是它只爲應用程序提供了有限的選擇。若是一個應用程序須要對CORE DOMAIN使用一種很是不一樣的方法,那麼可插入式組件框架將起到妨礙做用。
一、最小化
二、溝通和自律
三、經過重構獲得柔性設計
四、經過精煉能夠減輕負擔
【學習心得】:若是是在持續用心作事的話,每一次重構都是爲了比原來的更好,以上的幾種模式多少都會觸碰獲得,若是系統足夠「大且運行良好的話。固然,這本書會給了我更加寬廣的視野。
大型結構和精煉的概念也是互爲補充的。大型結構能夠幫助解釋CORE DOMAIN內部的關係以及GENERIC SUBDOMIAN之間的關係。同時大型結構自己可能也是CORE DOMAIN的一個重要部分。
當對一個項目進行戰略設計時,首先須要清洗地評估現狀。
(1)畫出CONTEXT MAP。你能畫出一個一致的圖嗎?有沒有一些模棱兩可的狀況?
(2)注意項目上的語言使用。有沒有UBIQUITOUS LANGUAGE?這種語言是否足夠豐富,以便幫助開發?
(3)理解重點所在。CORE DOMAIN被識別出來了嗎?有沒有DOMAIN VISION STATEMENT?你能寫一個嗎?
(4)項目所採用的技術是遵循MODEL-DRIVEN DESIGN,仍是與之相悖?
(5)開發團隊是否具有必要的技能?
(6)開發人員是否瞭解領域知識?他們對領域是否感興趣?
固然,咱們不會發現完美的答案。咱們如今對項目的瞭解永遠不如未來的瞭解深刻。但這些問題爲咱們提供了一個可靠的起點。
傳統上,架構是在應用程序開發開始以前創建的,而且在這種組織中,負責創建架構的團隊比應用開發團隊擁有更大的權利。但咱們並不必定的遵循這種傳統的方式,由於它並不老是十分有效。
戰略設計必須明確地應用於整個項目。項目有不少組織方式,這一點我並不想作過多的說明。可是,要想使決策制定過程更有效,須要注意一些基本問題。
一個很是善於溝通、懂得自律的團隊在沒有核心領導的狀況下照樣可以很好地工做,他們可以遵循EVOLVING ORDER來達成一組共同遵照的原則,這樣就可以有機地造成一種秩序,而不用靠命令來約束。
當幾個團隊共用同一種策略時,確實須要集中制定一些決策。架構師若是脫離實際開發工做,就可能會設計出失敗的模型,但這是徹底能夠避免的。架構團隊能夠把本身放在與應用開發團隊平等的位置上,幫助他們協調大型架構、BOUNDED CONTEXT邊界和其餘一些跨團隊的技術問題。爲了在這個過程當中發揮做用,架構團隊必須把思考重點放在應用程序的開發上。
一、決策必須傳達整個團隊;
二、決策過程必須收集反饋意見;
三、計劃必須容許演變;
四、架構團隊沒必要把全部最好、最聰明的人員都吸取進來;
五、戰略設計須要遵照簡約和謙遜的原則;
六、對象的職責要專注,而開發人員應該是多面手。
技術框架提供了基礎設施層,從而使應用程序沒必要本身去實現基礎服務,並且技術框架還能幫助把領域與其餘關注點隔離開,所以它可以極大地加速應用程序(包括領域層)的開發。但技術框架也有風險的,那就是它會影響領域模型實現的表達能力,並妨礙領域模型的自由改變。
不要編寫「傻瓜式」的框架。
在劃分團隊時,若是認爲一些開發人員不夠聰明,沒法勝任設計工做,而讓他們去作開發工做,那麼這種態度可能會致使失敗,由於他們低估了應用程序開發的難度。
由Christopher Alexander領導的一羣建築師(設計大樓的建築師)在建築和城市規劃領域中提倡「聚少成多地成長」(piecemeal growth)。他們很是好地解釋了整體規劃失敗的緣由。
若是沒有某種規劃過程,那麼俄勒岡州大學的校園永遠不會像劍橋大學校園那樣龐大、和諧而層次分明。
整體規劃是解決這種難題的傳統方法。它試圖創建足夠多的指導方針,來保持總體環境的一致性,同時仍然爲每幢建築保留自由度,併爲適應局部須要預留下廣闊的空間。
······未來這所大學的全部部分將構成一致的總體,由於它們只是被「插入」到整體設計的各個位置中。
······實際上整體規劃會失敗,由於它只是創建了一種極權主義的秩序,而不是一種有機的秩序。它們過於生硬,所以不容易根據天然變化和不可預料的社會生活變化來作出調整。當這些變化發生時······整體規劃就過期了,並且再也不被人們遵照。即便人們遵照整體規劃······它們也沒有足夠詳細地指定建築物以前的聯繫,人口規模、功能均衡等這些用來幫助每幢建築的局部行爲和設計很好地符合總體環境的方面。
······試圖駕馭這種整體規劃過程很是相似於在小孩的填色本上填充顏色······這個過程最多也不過是獲得一種極爲日常的秩序。
······所以,經過整體規劃是沒法獲得一種有機的秩序的,由於這個規劃既過於精確,又不夠細緻。它在總體上過於精確了,而細節上又不夠細緻。
······整體規劃的存在疏遠了用戶[由於,從根本上講]大部分重要決策已經肯定下來,所以社區成員對社區將來的建設幾乎沒有什麼影響了。
——摘自Oregon Experiment,PP. 16-28 [Alexander et al. 1975]
Alexander和他的同事倡議由社區成員共同制定一組原則,並在「聚少成多地成長」的每次行動中多應用這些原則,這樣就會造成一種「有機秩序」,而且可以根據環境變化作出調整。
【學習心得】:因爲我閱讀的集中力不足,因此沒法很好地從一次閱讀中獲取系統性的認知。所以,我必須用抄寫去深刻我心。特別是一些重要且很重要的知識,我必須這麼作。雖然費時費力,但用將來的眼光去看,當下是值得的,再用當下的眼光看將來,原來我如今作的都是對的。笨一點不要緊,時間就這麼用的。