一 領域驅動設計-運用領域模型-消化知識

来源:https://www.cnblogs.com/zhijiancanxue/archive/2020/03/18/12518180.html

[toc] 運用領域模型 消化知識 非原創,感謝《領域驅動設計》這本書 有效建模的要素 (1) 模型和實現的綁定。最初的原型雖然簡陋,但它在模型與實現之間建立了早期鏈接,而且在所有後續的迭代中我們一直在維護該鏈接。 (2) 建立了一種基於模型的語言。隨著項目的進展,雙方都能夠直接使用模型中的術語,並 ...


運用領域模型-消化知識

非原創,感謝《領域驅動設計》這本書

有效建模的要素

(1) 模型和實現的綁定。最初的原型雖然簡陋,但它在模型與實現之間建立了早期鏈接,而且在所有後續的迭代中我們一直在維護該鏈接。

(2) 建立了一種基於模型的語言。隨著項目的進展,雙方都能夠直接使用模型中的術語,並將它們組織為符合模型結構的語句,而且無需翻譯即可理解互相要表達的意思。

個人理解:定義專業詞語的字典解釋,保證每個人對每個術語的理解都是一樣的

(3) 開發一個蘊含豐富知識的模型。對象具有行為和強制性規則。模型並不僅僅是一種數據模式,它還是解決複雜問題不可或缺的部分。模型包含各種類型的知識。

(4) 提煉模型。在模型日趨完整的過程中,重要的概念不斷被添加到模型中,但同樣重要的是,不再使用的或不重要的概念則從模型中被移除。當一個不需要的概念與一個需要的概念有關聯時,則把重要的概念提取到一個新模型中,其他那些不要的概念就可以丟棄了。

(5) 頭腦風暴和實驗。語言和草圖,再加上頭腦風暴活動,將我們的討論變成“模型實驗室”,在這些討論中可以演示、嘗試和判斷上百種變化。當團隊走查場景時,口頭表達本身就可以作為所提議的模型的可行性測試,因為人們聽到口頭表達後,就能立即分辨出它是表達得清楚、簡捷,還是表達得很笨拙。

正是頭腦風暴和大量實驗的創造力才使我們找到了一個富含知識的模型並對它進行提煉,在這個過程中,基於模型的語言提供了很大幫助,而且貫穿整個實現過程中的反饋閉環也對模型起到了“訓練”作用。這種知識消化將團隊的知識轉化為有價值的模型。

知識消化

金融分析師要消化理解的內容是數字。他們篩選大量的詳細數字,對其進行組合和重組以便尋求潛在的意義,查找可以產生重要影響的簡單表示方式——一種可用作金融決策基礎的理解。

高效的領域建模人員是知識的消化者。他們在大量信息中探尋有用的部分。他們不斷嘗試各種信息組織方式,努力尋找對大量信息有意義的簡單視圖。很多模型在嘗試後被放棄或改造。只有找到一組適用於所有細節的抽象概念後,工作才算成功。這一精華嚴謹地表示了所發現的最為相關的知識。

知識消化並非一項孤立的活動,它一般是在開發人員的領導下,由開發人員與領域專家組成的團隊來共同協作。他們共同收集信息,並通過消化而將它組織為有用的形式。信息的原始資料來自領域專家頭腦中的知識、現有系統的用戶,以及技術團隊以前在相關遺留系統或同領域的其他項目中積累的經驗。信息的形式也多種多樣,有可能是為項目編寫的文檔,有可能是業務中使用的文件,也有可能來自大量的討論。早期版本或原型將經驗反饋給團隊,然後團隊對一些解釋做出修改。

在傳統的瀑布方法中,業務專家與分析員進行討論,分析員消化理解這些知識後,對其進行抽象並將結果傳遞給程式員,再由程式員編寫軟體代碼。由於這種方法完全沒有反饋,因此總是失敗。分析員全權負責創建模型,但他們創建的模型只是基於業務專家的意見。他們既沒有向程式員學習的機會,也得不到早期軟體版本的經驗。知識只是朝一個方向流動,而且不會累積。

個人理解:現在依然有公司在這樣做,業務需求人員定義需求,再由架構師或者組長進行翻譯,之後直接分配給程式員任務,最後做出的程式往往和需求偏差較遠,大部分會議是組行和業務參加,真正實現功能的程式員沒有參與到會議討論中來。

有些項目使用了迭代過程,但由於沒有對知識進行抽象而無法建立起知識體系。開發人員聽專家們描述某項所需的特性,然後開始構建它。他們將結果展示給專家,並詢問接下來做什麼。如果程式員願意進行重構,則能夠保持軟體足夠整潔,以便繼續擴展它;但如果程式員對領域不感興趣,則他們只會瞭解程式應該執行的功能,而不去瞭解它背後的原理。雖然這樣也能開發出可用的軟體,但項目永遠也不會從原有特性中自然地擴展出強大的新特性。

好的程式員會自然而然地抽象並開發出一個可以完成更多工作的模型。但如果在建模時只是技術人員唱獨角戲,而沒有領域專家的協作,那麼得到的概念將是很幼稚的。使用這些膚淺知識開發出來的軟體只能做基本工作,而無法充分反映出領域專家的思考方式。

在團隊所有成員一起消化理解模型的過程中,他們之間的交互也會發生變化。領域模型的不斷精化迫使開發人員學習重要的業務原理,而不是機械地進行功能開發。領域專家被迫提煉自己已知道的重要知識的過程往往也是完善其自身理解的過程,而且他們會漸漸理解軟體項目所必需的概念嚴謹性。

有這些因素都促使團隊成員成為更合格的知識消化者。他們對知識去粗取精。他們將模型重塑為更有用的形式。由於分析員和程式員將自己的知識輸入到了模型中,因此模型的組織更嚴密,抽象也更為整潔,從而為實現提供了更大支持。同時,由於領域專家也將他們的知識輸入到了模型中,因此模型反映了業務的深層次知識,而且真正的業務原則得以抽象。

模型在不斷改進的同時,也成為組織項目信息流的工具。模型聚焦於需求分析。它與編程和設計緊密交互。它通過良性迴圈加深團隊成員對領域的理解,使他們更透徹地理解模型,並對其進一步精化。模型永遠都不會是完美的,因為它是一個不斷演化完善的過程。模型對理解領域必須是切實可用的。它們必須非常精確,以便使應用程式易於實現和理解。

個人理解:對於業務和開發來說,最大的問題是思維想法的同步,同步最大的問題是交流溝通,程式員大部分相對內向沉默,業務有時候也會厭煩重覆的表述,造成理解偏差。建立模型並共同改進精化可以很好的解決這個問題,既然開發人員的思維方式(機器思維)和業務人員的思維方式(功能結果導向思維)不同,那麼就使用一個中間翻譯的語言,這個語言就叫做模型,也是抽象的模型。是大家一起來制定的一種抽象表達方式,每個人都要參與其中,並且不斷進行更新。模型也充當了業務轉化為開發邏輯的一種中間過程表示。

持續學習

當開始編寫軟體時,其實我們所知甚少。項目知識零散地分散在很多人和文檔中,其中夾雜著其他一些無關信息,因此我們甚至不知道哪些知識是真正需要的知識。看起來沒什麼技術難度的領域很可能是一種錯覺——我們並沒意識到不知道的東西究竟有多少。這種無知往往會導致我們做出錯誤的假設。

同時,所有項目都會丟失知識。已經學到了一些知識的人可能幹別的事去了。團隊可能由於重組而被拆散,這導致知識又重新分散開。被外包出去的關鍵子系統可能只交回了代碼,而不會將知識傳遞迴來。而且當使用典型的設計方法時,代碼和文檔不會以一種有用的形式表示出這些來之不易的知識,因此一旦由於某種原因人們沒有口頭傳遞知識,那麼知識就丟失了。

個人理解:團隊的解散,人員的離職或變更,文檔的丟失和散亂,都會使項目組丟失知識,有時候代碼寫完了但是沒有文檔或者註釋(程式員黑話:先開發產品功能,後寫文檔,其實就是懶得寫),別人接手需要經歷非常大的痛苦,然後瘋狂吐槽以前的開發人員。這些都可以歸於知識的丟失。

高效率的團隊需要有意識地積累知識,並持續學習。對於開發人員來說,這意味著既要完善技術知識,也要培養一般的領域建模技巧。但這也包括認真學習他們正在從事的特定領域的知識。那些善於自學的團隊成員會成為團隊的中堅力量,涉及最關鍵領域的開發任務要靠他們來攻剋,這個核心團隊頭腦中積累的知識使他們成為更高效的知識消化者。

關鍵的模型元素被保留下來,早期工作啟動了知識消化的過程,這使得所有後續工作更加高效:團隊成員、開發人員和領域專家等都學到了知識,他們開始使用一種公共的語言,而且形成了貫穿整個實現過程的反饋閉環。

個人理解:所有人都參與,制定模型,學習知識(業務邏輯或者開發思維或者處理流程等等都是知識),後期團隊效率會非常高,就算人員變更,只需要理解開發過程中一起制定的模型就可以了。

知識豐富的設計

當我們的建模不再局限於尋找實體和值對象時,我們才能充分吸取知識,因為業務規則之間可能會存在不一致。領域專家在反覆研究所有規則、解決規則之間的矛盾以及以常識來彌補規則的不足等一系列工作中,往往不會意識到他們的思考過程有多麼複雜。軟體是無法完成這一工作的。正是通過與軟體專家緊密協作來消化知識的過程才使得規則得以澄清和充實,並消除規則之間的矛盾以及刪除一些無用規則。

個人理解:業務或者產品如果不懂開發,會提出一些天馬行空的功能,不會意識到他們的想法實現起來由多複雜。比如:產品經理要程式要開發一個功能,要求app根據手機殼的顏色而改變主題顏色(新聞有報道,產品經理被打了,哈哈哈)。大家一起緊密協作來消化知識,可以消除這些矛盾或者無用的想法。

示例

我們從一個非常簡單的領域模型開始學習,基於此模型的應用程式用來預訂一艘船在一次航程中要運載的貨物

們規定這個應用程式的任務是將每件貨物(Cargo)與一次航程(Voyage)關聯起來,記錄並跟蹤這種關係。現在看來一切都還算簡單。應用程式代碼中可能會有一個像下麵這樣的方法

由於總會有人在最後一刻取消訂單,因此航運業的一般做法是接受比其運載能力多一些的貨物。這稱為“超訂”。有時使用一個簡單的容量百分比來表示,如預訂110%的載貨量。有時則採用複雜的規則——主要客戶或特定種類的貨物優先。這是航運領域的一個基本策略,從事航運業的業務人員都知道它,但在軟體團隊中可能不是所有技術人員都知道這條規則。需求文檔中包含下麵這句話:允許10%的超訂。(這個時候由問題了哈)

現在,一條重要的業務規則被隱藏在上面這段方法代碼內,非常容易誤解,我們主要考慮如何把這條規則更清楚地表達出來,並讓項目中的每個人都能瞭解到它。這將使我們得到一個類似的解決方案。

(1) 如果業務規則如上述代碼所寫,不可能有業務專家會通過閱讀這段代碼來檢驗規則,即使在開發人員的幫助下也無法完成。

(2) 非業務的技術人員很難將需求文本與代碼聯繫起來。如果規則更複雜,情況將更糟。

修改成下麵這樣:

現在所有人都清楚超訂是一個獨特的策略,而且超訂規則的實現即明確又獨立。

現在,我並不建議將這樣的精細設計應用到領域的每個細節中。第15章將深入闡述如何關註重點以及如何隔離其他問題或使這些問題最小化。這個例子的目的是說明領域模型和相應的設計可用來保護和共用知識。更明確的設計具有以下優點:

(1) 為了實現更明確的設計,程式員和其他各位相關人員都必須理解超訂的本質,明白它是一個明確且重要的業務規則,而不只是一個不起眼的計算。

(2) 程式員可以向業務專家展示技術工件,甚至是代碼,但應該是領域專家(在程式員指導下)可以理解的,以便形成反饋閉環。

個人理解:模型要把重要的細節表達出來,不能隱藏的自己知道的認知中(就不告訴別人,說你這樣做就行了,這樣不好),要體現出為什麼是這樣的,是什麼導致必須要加入這個,要讓模型在成為溝通的橋梁時更加明確易懂。

深層模型

有用的模型很少停留在錶面。隨著對領域和應用程式需求的理解逐步加深,我們往往會丟棄那些最初看起來很重要的錶面元素,或者切換它們的角度。這時,一些開始時不可能發現的巧妙抽象就會漸漸浮出水面,而它們恰恰切中問題的要害。

前面的例子大體上是基於一個集裝箱航運項目,這是本書列舉的幾個項目之一,本書還有幾個示例會引用這個項目。本書所舉的示例都很簡單,即使不是航運專家也能理解它們。但在一個需要團隊成員持續學習的真實項目中,要想建立實用且清晰的模型則要求團隊成員既精通領域知識,也要精通建模技術。

在這個項目中,由於航運從預訂貨運開始,因此我們開發了一個能夠描述貨物和運貨航線等事物的模型。這是必要且有用的,但領域專家卻不買賬。他們有自己的考慮業務的方式,這種方式是我們沒有考慮到的。

最後,在經過幾個月的知識消化後,我們知道貨物的處理主要是由轉包商或公司中的操作人員完成的,這包括實際的裝貨、卸貨和運貨。航運專家的觀點是,各部分之間存在一系列的責任傳遞。法律責任和執行責任的傳遞由一個過程式控制制—從托運人傳遞到某個本地運輸商,再從這家運輸商傳遞到另一家運輸商,最後到達收貨人。通常,在一些重要的步驟中,貨物停放在倉庫里。在其他時間里,貨物則是通過複雜的物理步驟來運輸,而這些與航運公司的業務決策無關。在處理航線的物流之前,必須先確定諸如提單等法律文件以及支付流程。

對航運業務有了更深刻的認識後,我們並沒有刪除Itinerary(航線)對象,但模型發生了巨大改變。我們對航運業務的認識從“集裝箱在各個地點之間的運輸”轉變為“運貨責任在各個實體之間的傳遞”。處理這些責任傳遞的特性不再是一些附屬於裝貨作業的次要特性,而是由一個獨立的模型來提供支持,這個模型正是在理解了作業與責任之間的重要關係之後開發出來的。

知識消化是一種探索,它永無止境。

個人理解:隨著對業務的理解更加深刻以後,模型同時也要進行更新,會變得複雜,怎麼辦?開發容易理解的一種形式是:就相當於sql中做子查詢,java開發中就小模塊封裝,大模塊調用函數。模型也是一樣,再做一個新模型,對某一部分功能做抽象,由大模型鏈接過去。例如:a-b-c原來是一個模型,後來增加到a-b-c-c1-c2-c3,c的功能原來越多,這個時候可以做成這樣:模型1是a-b-C,模型C(獨立小模型):c1-c2-c3。這就是深層模型的理解。


您的分享是我們最大的動力!

更多相關文章
  • 故事背景: 沙雕在公司一個開放API團隊工作,平時的工作內容主要是做一些對外API項目開發,負責跟第三方系統對接。雖然工作也幾年了,但是一直都是按照上級做好的設計文檔和分配的任務去做,很少自己去做設計和思考。有一天團隊老大跑路了,部門老闆想提拔他做團隊老大的工作,但需要負責一個新項目架構設計工作來試 ...
  • 策略模式(Strategy):它定義了演算法家族,分別封裝起來,讓它們之間可以互相替換,此模式讓演算法的變化不會影響到使用演算法的客戶。 ——《大話設計模式》 策略模式主要用來解決當有多種相似演算法的時,使用if...else產生的難以維護的問題。它主要由三部分組成:Strategy介面、具體的Strate ...
  • 圖解Java設計模式之橋接模式 手機操作問題 傳統方案解決手機操作問題 傳統方案解決手機操作問題分析 橋接模式(Bridge)-基本介紹 橋接模式解決手機操作問題 橋接模式在JDBC中的源碼解析 橋接模式的註意事項和細節 橋接模式其它應用場景 手機操作問題 現在對不同手機類型的不同品牌實現操作編程( ...
  • [toc] 領域驅動設計 領域對象的生命周期 每個對象都有生命周期,如圖6 1所示。對象自創建後,可能會經歷各種不同的狀態,直至最終消亡——要麼存檔,要麼刪除。當然,很多對象是簡單的臨時對象,僅通過調用構造函數來創建,用來做一些計算,而後由垃圾收集器回收。這類對象沒必要搞得那麼複雜。但有些對象具有更 ...
  • [toc] 軟體中所表示的模型 表示模型的3種模型元素模式:ENTITY、VALUE OBJECT和SERVICE。從錶面上看,定義那些用來捕獲領域概念的對象很容易,但要想反映其含義卻很困難。這要求我們明確區分各種模型元素的含義,並與一系列設計實踐結合起來,從而開發出特定類型的對象。 個人理解:就是 ...
  • [toc] 模型驅動設計的構造塊 分離領域 在軟體中,雖然專門用於解決領域問題的那部分通常只占整個軟體系統的很小一部分,但其卻出乎意料的重要。我們需要著眼於模型中的元素並且將它們視為一個系統。絕不能像在夜空中辨認星座一樣,被迫從一大堆混雜的對象中將領域對象挑選出來。我們需要將領域對象與系統中的其他功 ...
  • [toc] 領域驅動設計 運用領域模型 綁定模型和實現 聰明的項目組成員花費了幾個月的時間進行仔細的研究並且開發出了詳盡的領域模型(類圖)。然而對類圖研究不能讓我深入地瞭解該應用程式的代碼和設計,這讓我備感困擾。當開發人員開始實現應用程式時,他們很快就發現,儘管分析人員說得頭頭是道,他們依然無法將這 ...
  • [toc] 運用領域模型 交流與語言的使用 非原創,感謝《領域驅動設計》這本書 領域模型可成為軟體項目通用語言的核心。該模型是一組得自於項目人員頭腦中的概念,以及反映了領域深層含義的術語和關係。這些術語和相互關係提供了模型語言的語義,雖然語言是為領域量身定製的,但就技術開發而言,其依然足夠精確。正是 ...
一周排行
  • 前幾天發佈了 "抄抄《CSS 故障藝術》的動畫" 這篇文章,在這篇文章里介紹瞭如何使用Win2D繪製文字然後配合BlendEffect製作故障藝術的動畫。本來打算就這樣收手不玩這個動畫了,但後來又發現性能不符合理想。明明只是做做Resize動畫和用BlendEffect混合,為什麼性能會這麼差呢? ...
  • 控制條控制項: progressBar 不能按照你程式的進程自動變化,需認為計算,調整變化量 private void progressBar1_Click(object sender, EventArgs e) { this.progressBar1.Maximum = 100;//設置進度條最大長 ...
  • 首先創建一個asp.net core web應用程式 第二步 目前官方預置了7種模板項目供我們選擇。從中我們可以看出,既有我們熟悉的MVC、WebAPI,又新添加了Razor Page,以及結合比較流行的Angular、React前端框架的模板項目。 空項目模板 Program.cs using S ...
  • 對閉包的理解 1.對於成員變數和局部變數:成員變數就是方法外部,類的內部定義的變數;局部變數就是方法或語句塊內部定義的變數。局部變數必須初始化。 形式參數是局部變數,局部變數的數據存在於棧記憶體中。棧記憶體中的局部變數隨著方法的消失而消失。成員變數存儲在堆中的對象裡面,由垃圾回收器負責回收。 成員變數它 ...
  • Xamarin.Forms讀取並展示Android和iOS通訊錄 TerminalMACS客戶端 本文同步更新地址: https://dotnet9.com/11520.html https://terminalmacs.com/861.html 閱讀導航: 一、功能說明 二、代碼實現 三、源碼獲取 ...
  • 做下對文件複製操作相關的筆記: /// <summary> /// 文件幫助類 /// </summary> public class FileHelper { /// <summary> /// 複製一個目錄下所有文件到一個新目錄下 /// </summary> /// <param name=" ...
  • 前言 有一個東西叫做鴨子類型,所謂鴨子類型就是,只要一個東西表現得像鴨子那麼就能推出這玩意就是鴨子。 C 裡面其實也暗藏了很多類似鴨子類型的東西,但是很多開發者並不知道,因此也就沒法好好利用這些東西,那麼今天我細數一下這些藏在編譯器中的細節。 不是只有 和 才能 在 C 中編寫非同步代碼的時候,我們經 ...
  • [toc] 1.應用背景 底端設備有大量網路報文(位元組數組):心跳報文,數據採集報文,告警報文上報。需要有對應的報文結構去解析這些位元組流數據。 2.結構體解析 由此,我第一點就想到了用結構體去解析。原因有以下兩點: 2.1.結構體存在棧中 類屬於引用類型,存在堆中;結構體屬於值類型,存在棧中,在一個 ...
  • 《深入淺出 C#》 (第3版) [作者] (美) Andrew Stellman (美) Jennifer Greene[譯者] (中) 徐陽 丁小峰 等譯[出版] 中國電力出版社[版次] 2016年08月 第1版[印次] 2018年04月 第4次 印刷[定價] 148.00元 【引子】 要學習編程 ...
  • 記錄使用對象初始值設定項初始化對象。 using System; using System.Collections.Generic; namespace ConsoleApp2 { class Program { static void Main(string[] args) { // 使用構造函數 ...
x