DDD的哲學:核心域、統(tǒng)一語言
作者 | 鐘敬
閱讀本系列文章:《??DDD的哲學:模型的關聯、演進和認知??》
核心域與主要矛盾
大約公元前800年至前200年間,中國、希臘、印度和以色列的文明幾乎在同一時期興起,這被稱為人類文明的軸心時代。不同文明展現出了不同的風貌。中國古代文化強調辯證邏輯,重視變化、聯系和綜合的思維方式,同時又有“子不語怪力亂神,六合之外存而不論”的唯物主義傾向。古希臘則重視嚴格的推理和分析,孕育了形式邏輯和公理化的數學體系。印度在婆羅門和佛教哲學中,從另一個方向將辯證法、邏輯和語言學推向極致。以色列的先知則創(chuàng)立了一神論的猶太教和政教合一的社會體制。
直到近代,西方才產生了系統(tǒng)的辯證法體系,集大成者是黑格爾。馬克思和恩格斯吸收了黑格爾的辯證法,揚棄了其中的唯心主義成分,形成了辯證唯物主義,成為了風起云涌的無產階級革命的理論基礎。后來唯物辯證法傳入中國,與中國傳統(tǒng)文化中的辯證法和唯物主義思想一拍即合。為了將馬克思主義中國化,毛老師寫出了《實踐論》和《矛盾論》。前文提到了《矛盾論》第一部分“兩種宇宙觀”,這一節(jié)看一下第四部分“主要的矛盾和主要的矛盾方面”。
事物中包含著多種矛盾,其發(fā)展變化成為事物發(fā)展的內在動力。在多種矛盾中,一般會有一種占主導地位,稱為“主要矛盾”。要有效地處理問題,就要抓住主要矛盾。
例如某籌備開業(yè)的保險公司要開發(fā)一個保險核心系統(tǒng)。在開始時,“快速開展新單業(yè)務的需要與落后的新單處理能力”的矛盾就是主要矛盾,而“快速理賠的要求與落后的理賠能力”之間的矛盾則是次要矛盾。這是因為,新開業(yè)的保險公司首先面臨的是新單業(yè)務的壓力而不是理賠的壓力。
在領域驅動設計中,領域模型中包含主要矛盾的部分,稱為“核心域”(Core Domain)。在復雜的領域模型中,將核心域識別并有效處理的過程稱為“精煉”(Distillation, 《領域驅動設計》第15章)。領域驅動設計中還提供了“領域愿景說明”(Domain Vision Statement),“強調核心” (Highlighted Core), “分離核心”(Segregated Core)等模式用于精煉過程的具體實踐。
隨著內因和外因的變化,主要矛盾和次要矛盾會發(fā)生轉化。例如,當處理新單業(yè)務的系統(tǒng)成熟后,就成為了次要矛盾,而理賠則成為主要矛盾。當理賠也成熟后,靈活的產品和算法的定義可能變?yōu)橹饕堋?/p>
總之,以矛盾的觀點看問題,就可以知道識別和處理“核心域”的必要性,同時以發(fā)展的眼光看待核心域的轉化。
這里還有一個微妙的問題:目前業(yè)界不同人所說的“核心域”的含義是有區(qū)別的。假如我們把業(yè)務需求作為“問題空間”,將軟件系統(tǒng)的分析和設計作為“解空間”,那么《領域驅動設計》一書中所說的核心域實際上處于解空間,而另一本《實現領域驅動設計》中的核心域說的是問題空間。
《領域驅動設計》中,核心域在“精煉”一章中。其邏輯在于,先有領域模型,然后在領域模型發(fā)展到一定的復雜程度時,就需要從中“精煉”出核心域了。由于領域模型處于解空間,因此《領域驅動設計》中的“核心域”談的是解空間的問題。
而《實現領域驅動設計》中的核心域則來自于對業(yè)務問題的分解,因此屬于問題空間,先于領域模型的建立。
存在兩個層面的“核心域”的現象具有一定的合理性。因為矛盾是普遍存在的,既存在于問題空間,也存在于解空間,兩者都有“主要矛盾”,所以可以說兩個層面都存在核心域。
目前多數人理解的核心域是《實現領域驅動設計》中的說法,而《領域驅動設計》原書中的模式和實踐卻沒有得到重視。領域驅動設計本來要求“統(tǒng)一語言”,但“核心域”這一概念自身已經不統(tǒng)一了。這是業(yè)界需要解決的一個問題。
下一節(jié)就來看看與統(tǒng)一語言相關的問題吧。
統(tǒng)一語言與哲學的“語言轉向”
前面介紹了哲學從本體論向方法論的轉變。在方法論方面,洛克、休謨、帕斯卡等等大家宛如哲學天空中的星斗,而黑格爾和康德是其中最璀璨的兩顆。
然而新的問題來了。這些哲學家的著作中往往充斥著晦澀的詞匯,其中很多是自造的,在此基礎上又進行了復雜的推演,然后產生了更加晦澀的語言。這些莫衷一是的概念又被后人按照自己的意思做了不同的解釋。后果是,不僅普通人難以窺其門徑,即便專業(yè)的哲學工作者,也會爭論不休。盡管由此產生了大量論文和“學術成果”,但人們也逐漸意識到,很多爭論僅僅是由于對同一名詞的理解不同,而沒有解決任何實際問題。這樣的哲學除了在象牙塔中孤芳自賞,又有多少實際意義?
到了十九世紀末,人們逐漸意識到問題很可能出在“語言”上,很多哲學問題來源于對語言的誤用。解決了語言問題,就解決了哲學問題。由此產生了“語言哲學”,并成為了二十世紀上半葉英美哲學的主流。如果說認識論是“對思考的思考”,那么語言哲學就是“對言說的言說”。盡管歷史上有很多哲學家也很重視語言,但那時語言只是研究哲學的工具;而現在,語言成了哲學本身。這種哲學關注點的轉移,稱為“哲學的語言轉向”(the linguistic turn in philosophy)。
信息技術和語言有著不解之緣。當您初次學習編程,知道了一條條指揮計算機工作的指令被稱為計算機“語言”時,是否曾像我當初那樣,有過一絲的驚訝?
在領域驅動設計中和語言直接相關的模式是“統(tǒng)一語言”(Ubiquitous Language)。該模式出現在《領域驅動設計》的第二章,屬于該書第一部分“運用領域模型”,與“消化知識”和“模型驅動設計”共同作為全書的總綱。
“統(tǒng)一語言”要求業(yè)務和開發(fā)人員要用一致的語言來溝通。例如,同一個概念應該用同一個詞匯,同一詞匯也僅表達唯一的概念。這一道理是如此“直白”,為什么領域驅動設計還要專門強調呢?因為這一實踐看似不難理解,但要在實踐中真正做好則不太容易。下面我們從語言哲學的角度看一看軟件開發(fā)中有關語言的問題。
首先,語言哲學認為,語言的意義是在使用的過程中體現出來的。
前面提到的早期哲學家,他們的語言是為了表達自己的觀念自造的,并沒有真正被用來解決實際問題,因而沒有“恰當”地使用語言,從而造成了新的哲學問題。
回到軟件開發(fā),如果對軟件的理解只是寫在文檔中,而這些文檔很少有機會被閱讀,沒有真正起到交流的作用,那么也可以看作對語言的誤用。這也正是敏捷軟件開發(fā)所反對的。而統(tǒng)一語言強調不同干系人,尤其是業(yè)務人員和開發(fā)人員間的溝通。只有在協作中,統(tǒng)一語言才有意義。
近年流行的“事件風暴”方法,也可看作是統(tǒng)一語言的應用。事件風暴和經典的用例分析方法所解決的問題其實是類似的,都是為了捕獲行為需求,為進一步的領域建模奠定基礎。事件風暴中的“風暴”來源于頭腦風暴,是常用的溝通協作方法。因此,溝通協作是事件風暴的內在組成部分,這一點是事件風暴優(yōu)于傳統(tǒng)的用例分析的地方。
其次,語言哲學強調語言的表達能力是有的限度的。
如維特根斯坦所說,“凡可說的,皆可說清;凡不可說的,應保持緘默”。前面提到的早期哲學家的另一個問題,就是強行用語言表達無法表達的東西。
領域驅動設計認為,軟件開發(fā)的過程可以看作知識的學習、構建和運用的過程。而有些知識是語言難以表達的。不過,難以表達不代表不可知。比如說,我們難以向沒有吃過梨子的人描述梨子的味道,但這并不代表梨子的味道是不可知的,只要嘗一下就可以了。軟件開發(fā)中的不可言說知識同樣不是玄學,而完全可以通過持續(xù)的交流和實踐來掌握。因此,不要期望通過文檔可以表達所有知識,而要回歸到干系人之間的溝通和協作。
再次,語言哲學認為,語言只有在特定的語境(Context)中才有意義。
前文已經提到,領域驅動設計中利用限界上下文(Bounded Context)來保證概念的一致性。而統(tǒng)一語言是針對限界上下文而言的,只有在其所處的上下文中才有意義。英文Context在中文中有兩種譯法:“語境”和“上下文”。目前《領域驅動設計》一書中采用了“上下文”的譯法。其實譯作“語境”可能更恰當,在“語境”中運用“語言”,意思會更協調一些。
最后,語言哲學又可分成兩大流派:較早的“理想語言學派”和晚一些的“日常語言學派”。
理想語言學派認為自然語言是模糊的,哲學的出路在于利用數理邏輯創(chuàng)造一種形式化的語言。這種語言不僅表意明確,而且可以跨越各種自然語言。這在后來導致了計算機科學的基礎理論“形式語言與自動機”的產生。
這一過程可以追溯到十七至十八世紀,萊布尼茨發(fā)明的二進制以及提出的“通用文字”(characteristica universalis)的構想。到了十九世紀,布爾利用二進制使邏輯成為了數學演算。布爾代數至今仍然是計算機科學的基礎。稍后,現代邏輯學之父弗雷格提出了一階謂詞演算。這一理論也導致了像Prolog這樣的邏輯編程語言的誕生。二十世紀,羅素、哥德爾等人進一步將數理邏輯發(fā)揚光大。后來,圖靈提出的“圖靈機”,丘奇提出的lambda演算,成為了算法和編程語言的數學基礎。我們程序員所使用的各種編程語言,也可以算作形式語言。
較晚出現的一派則認為自然語言本身就是精確的,之所以會讓人覺得模糊,是因為沒有進入特定的語境。因此,應通過對自然語言本身的研究處理哲學問題,而無需借助形式語言。這就是“日常語言學派”。
對于軟件開發(fā)者來說,沒有必要陷入哲學家的爭論。我們反而可以看到兩者各自的合理性,以及互相銜接的可能。
一方面,計算機中運行的是二進制的字節(jié)流,而這又是由編程語言編譯得來的。機器語言和編程語言都是形式語言。另一方面,計算機所要解決的業(yè)務問題,卻開始于日常語言或者說自然語言的描述。因此我們必須有一種自然語言和形式語言的轉換機制。領域驅動設計中的領域建模正是這一機制的重要環(huán)節(jié)。領域模型在自然語言和編程語言之間建立起了橋梁,幫助跨越兩者之間的巨大鴻溝,成為開發(fā)復雜軟件的重要手段。
領域驅動設計中,常用UML(統(tǒng)一建模語言)進行領域建模。嚴格地說,UML也是一種形式語言。不過比起編程語言,UML更貼近業(yè)務概念,更容易與自然語言轉換。同時,用UML建立的領域模型,又可以方便地映射到編程語言和數據庫設計。UML常用于軟件系統(tǒng)的分析和設計,而編程語言用于編碼。在分析、設計、編碼過程中保持語言的統(tǒng)一,是統(tǒng)一語言的另一層意思。
小結
我們用若干章節(jié)探討了領域驅動設計的哲學內涵。兩者之間的這種契合關系并非偶然。
哲學的主要目的是解決對世界進行認識(認識論)和詮釋(語言哲學)問題。前面說的本體論,從現在的觀點來看也可以理解為一種對世界的詮釋。
計算機系統(tǒng)本質上是用軟硬件對現實中的事物以及邏輯進行模擬,進而解決現實中的問題。計算機軟件可以看作現實世界的模型。在計算機中最終運行的模型是二進制流,這在形式上與現實世界有巨大的差距,這是軟件開發(fā)的本質復雜性之一。
為了跨越現實世界和二進制模型之間的鴻溝,軟件開發(fā)方法學采取了漸進的方式。首先將現實世界映射為分析模型,然后由分析模型映射為設計模型,再映射為編程語言模型,最后編譯成二進制模型。通過這種逐層遞進的方式,來化解軟件開發(fā)的困難。
領域驅動設計中所說的領域模型,主要指上面說的分析模型,目的是為了反應業(yè)務概念,也兼顧了一定的開發(fā)視角。建立領域模型的過程,就是認識現實世界中的概念及其發(fā)展變化邏輯的過程,而這正是認識論和語言哲學的應用場景。這就是哲學對領域建模以及整個軟件開發(fā)過程具有指導作用的原因。
對領域建模的強調,并非始自領域驅動設計,而是從軟件開發(fā)方法學出現的時候就已經開始了。早期的結構化方法學,采用數據流圖和ER圖等方式進行建模。后來發(fā)展為面向對象方法學,采用對象建模的方式。領域驅動設計則是對面向對象方法學的歸納和發(fā)展,為面向對象方法學建立了一套模式語言,使之更容易學習和運用。
領域驅動設計主要面向的還是傳統(tǒng)意義上的軟件開發(fā),尤其是企業(yè)應用的開發(fā)。經過幾十年的發(fā)展,至少在理論上已經研究得比較透徹了,難點在于工程上的推廣和應用。而近年來,人工智能、大數據以及下一代互聯網的發(fā)展,帶來了一系列新的問題:隱私如何保護?誰擁有個人行為數據的所有權?人工智能是否會剝奪人的自由意志?怎樣判定人工智能作惡的法律責任?機器人可以用于戰(zhàn)場嗎?個人怎樣面對高科技所帶來的焦慮?等等。這些則是倫理學、心靈哲學所要討論的問題。這些話題超出了領域驅動設計的范圍,我們的討論,也可告一段落了。