領域模型在交流中扮演的角色

問題:我對於領域模型如何表示始終還不太明白。按照Evans書裏的說法,代碼應當是領域模型的主要部分,文檔、圖表作爲補充。另外一方面,領域模型應當是所有參與者都能夠理解的,而我覺得用戶不太可能去理解代碼。

比如以Evans書裏舉的,可以超載10%這一點,書裏是通過一個Strategy模式來表達這個知識,從程序員的角度看很清晰了,但是從用戶的角度看,還是不太能夠明白吧。

請教張老師如何看待這個問題?

回答:其實從模型的角度看,有幾個層次,Eric說的是模型驅動模型。重點是模型。這幾個層次包括:

  • 現實模型即問題域
  • 領域模型
  • 設計模型
  • 代碼模型

實際上領域模型是搭建現實模型需求問題到解決方案的橋樑。領域模型是領域概念尤其是統一語言的可視化表現,在Eric寫作《領域驅動設計》一書的時代,領域模型多數以UML來表達。

這裏要注意一個歷史問題。在Eric寫作該書的時代,正是UML與逆向工程大行其道的時代。當時有很多人都在倡導運用建模工具如Rational Rose來建模,進而利用圖形化的模型生成代碼。這個思想在當時人們的心中會是未來編程的一個主流發展方向,也有很多人在朝着這個方向努力,隨之也催生了諸多建模工具的誕生,UML得到大量的普及,甚至差點成爲了軟件設計的唯一標準。這也是Eric倡導模型驅動設計的一個歷史背景,至少我認爲他在寫書時是收到這個思想影響的。最終,這種設計思想並沒有得以實現,人們低估了編程的複雜度,高估了模型的重要性。所以,Eric的書是有歷史侷限性的。盡信書不如無書,這是閱讀他的書要注意的。書中講的一些實踐,未必都對。

但是,Eric的領域驅動設計是一個方法學,是開放的,也是逐步演進的。事實上,已經有很多人站在Eric的肩膀上,提出了很多切合實際,也吻合軟件行業發展趨勢的實踐與模式,作爲領域驅動設計的補充。例如領域事件、六邊形架構以及CQRS等。Eric自己也認可這種演進。

回到模型上來。我認爲領域模型就是對領域概念的抽象,你說的超載10%其實就是業務規則,所以可以抽象爲一個領域概念,在與領域專家進行交流時,可以通過領域模型的這個領域概念來表達,而不是直接使用代碼。

設計模型則是對領域模型的一種技術呈現,乃至於是從技術角度的一種精化與演進,例如通過引入設計原則與模式,可以實現領域模型對象更好的職責分配,通過抽象實現解耦,定義更加合理的封裝。這時,設計模型要取決於你的編程範式,如採用面向對象還是函數式編程。同樣以超載規則爲例,面向對象範式的設計模型就是抽象的服務接口,函數式就是一個函數。如果規則需要組合,前者就利用繼承或委派,後者就用組合子。

代碼模型是設計模型的具體實現,它是遵循設計模型來實現的,採用不同的語言和框架,也會有區別。例如,有的語言可以非常方便地定義值對象,如Scala的Case Class,就是值對象的語法糖。

整體來看,領域模型是團隊與領域專家交流所用,設計模型是團隊的設計人員交流的工具,代碼模型自然爲程序員服務。這三個模型之間的關係如下圖所示:

隨着時間的推移,這三種模型可能會出現不同步的問題。Eric在書中講解模型驅動設計時也提到了這個問題。如上圖所示,領域模型爲指導設計模型,設計模型是領域模型的實現,而隨着設計模型的演進,我們又需要這種變更體現在領域模型中,保證模型是領域的真實表達。至於代碼模型,一方面是遵循設計模型進行代碼的實現,同時還應該盡力保障代碼模型要表達領域概念,這不僅僅是從代碼可讀性的角度來考慮,也牽涉到代碼對領域邏輯的呈現。這也是爲什麼在DDD的編程實踐中,我們爲什麼希望避免貧血模型,希望避免使用無法表達領域行爲的get和set方法的原因。

倘若要在代碼模型中體現領域模型,一種更好的做法是使用DSL,即領域特定語言。但DSL的實現其實是一個相對漫長的積累過程,不同語言的領域表達能力也不相同。所以DSL主要還是用在一些相對複雜但又相對穩定專業的行業中,例如通信和金融行業,就有DSL的開發需求。當然,即使不去做一套DSL,我們也可以借鑑DSL的思想,例如通過Fluent Interface之類的實踐改進代碼的表達能力。

還有一種做法就是利用BDD編寫驗收測試,形成活文檔(Live Document)。BDD框架如Cucumber、Robot Framework、RSpec其實就是一種DSL,通過這些框架可以編寫符合自然語言規範的測試用例,形成一箇中規格(Specification),這些測試用例又是能夠運行的代碼,這就相當於搭建了代碼與需求規格的橋樑。不過,這種活文檔只能應用在測試保障上,它可以幫助我們建立一種更好的交流機制,但並不能取代設計模型和代碼模型。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章