作者 | 鐘敬
接上篇《??當(dāng)我們談?wù)揇DD時我們在談?wù)撌裁??》
“關(guān)聯(lián)”、《矛盾論》、畢達(dá)哥拉斯學(xué)派
DDD的哲學(xué)意味(上)說到了“模型驅(qū)動的設(shè)計”以及其中兩個重要的模式“實(shí)體”和“值對象”,兩者統(tǒng)稱“領(lǐng)域?qū)ο蟆?。在領(lǐng)域建模的過程中,建立領(lǐng)域?qū)ο箝g的“關(guān)聯(lián)(Association)”也是非常重要的?!禗DD》第5.1節(jié)對此進(jìn)行了專門的討論。不過與實(shí)體不同,艾老師并沒有把關(guān)聯(lián)當(dāng)做一種正式的“模式”。這一點(diǎn)實(shí)屬可惜,因?yàn)殛P(guān)聯(lián)至少與實(shí)體有同樣的重要性。為什么這么說呢?下面還是先扯幾句哲學(xué)。
前面提到毛老師的《實(shí)踐論》,這里再說說怹老人家的另一篇杰作《矛盾論》。這篇論文的第一部分“兩種宇宙觀”中提到了兩種認(rèn)識論體系:“形而上學(xué)”和“唯物辯證法”。前者是錯誤的、后者是正確的。
形而上學(xué)認(rèn)為事物的發(fā)展是靜態(tài)的、外因驅(qū)動的、孤立的;唯物辯證法則認(rèn)為事物的發(fā)展是動態(tài)的、內(nèi)因驅(qū)動的、聯(lián)系的(還記得中學(xué)政治課背過嗎?)。前兩點(diǎn)我們后面再聊,這里先討論“聯(lián)系”。
《矛盾論》中引用列寧的話:“要真正地認(rèn)識事物,就必須把握住、研究清楚它的一切方面、一切聯(lián)系和‘中介’。我們永遠(yuǎn)也不會完全做到這一點(diǎn),但是,全面性這一要求可以使我們防止犯錯誤和防止僵化?!边@強(qiáng)調(diào)了,只有充分了解事物之間的聯(lián)系,才能充分認(rèn)識事物。
DDD中,領(lǐng)域(事物)的概念以實(shí)體、值對象、聚合、模塊等方式表達(dá)出來。有些伙伴把領(lǐng)域中的主要聚合或?qū)嶓w識別出來后,卻沒有識別它們之間的關(guān)聯(lián),就認(rèn)為已經(jīng)完成了領(lǐng)域建模。這樣的模型其實(shí)是不完整的。那么,如果我們認(rèn)識到了這一點(diǎn),并且識別出了關(guān)聯(lián)之后,還要進(jìn)一步識別關(guān)聯(lián)上的哪些信息呢?讓我們先看一下古老的畢達(dá)哥拉斯學(xué)派。
這個學(xué)派在古希臘最早對數(shù)學(xué)進(jìn)行了系統(tǒng)的研究。他們發(fā)現(xiàn)了無理數(shù),然后把泄露無理數(shù)秘密的團(tuán)隊成員扔進(jìn)了大海。畢達(dá)哥拉斯認(rèn)為世界的本原是“數(shù)”。宇宙的和諧來源于數(shù)的和諧。而數(shù)的和諧體現(xiàn)在十組對立的關(guān)系中:一與多、奇與偶、左與右、陰與陽、動與靜、曲與直、明與暗、善與惡、方與長、有限與無限。在這十組關(guān)系中,最根本的是“一與多”。也就是說,只有把握了“一與多”,才能把握“數(shù)”,進(jìn)而把握世界。
從畢達(dá)哥拉斯的學(xué)說中,我們或許可以得到這樣的啟發(fā)(嗯 ~ ~ 我承認(rèn)有點(diǎn)牽強(qiáng)):在領(lǐng)域模型中,對關(guān)聯(lián)進(jìn)行建模時首先要考慮的是數(shù)量關(guān)系。也就是常說的“一對一”“一對多”“多對多”等等。如果做得精細(xì)些的話,還可以考慮存在性(必選的還是可選的)。不少建模人員在建模時都忽視了理清數(shù)量關(guān)系,從而限制了對領(lǐng)域知識的深刻理解。
對數(shù)量關(guān)系的識別看似簡單,但我們發(fā)現(xiàn)百分之六七十的初學(xué)者在實(shí)踐中都會搞錯。只有經(jīng)過一段時間的練習(xí),才能充分掌握。
此外,前文提過,除了這里說的實(shí)例之間的關(guān)聯(lián)以外,還要考慮類型之間的關(guān)聯(lián)(也就是泛化)。對泛化關(guān)系的掌握,是領(lǐng)域建模技術(shù)從初級階段邁向中級階段的門檻,也是向高級建模技術(shù)進(jìn)階的關(guān)鍵。要熟練掌握這項(xiàng)技術(shù),除了在項(xiàng)目中實(shí)踐,還可參考《分析模式》。在《DDD》的后半部分有一些高階的例子運(yùn)用了泛化,但在前面講模型驅(qū)動設(shè)計的章節(jié)卻沒有提及泛化,這就忽略了一種建模的重要手段,也算是該書的一個瑕疵吧。
模型的演進(jìn)、辯證法、進(jìn)化論
前文說到,唯物辯證法認(rèn)為事物的發(fā)展是動態(tài)的、內(nèi)因驅(qū)動的、聯(lián)系的;并且已經(jīng)討論了“聯(lián)系”。這一節(jié)首先討論“動態(tài)”。
事物的發(fā)展變化是永恒的。因此,我們會不斷強(qiáng)調(diào),與其期望一次性把領(lǐng)域模型和架構(gòu)建好,不如建立團(tuán)隊的架構(gòu)演進(jìn)能力。在《DDD》第8章至第13章,集中討論了模型重構(gòu)的方法。
模型要不斷演進(jìn),這個說法多數(shù)人聽了都會點(diǎn)頭稱是。又有誰不知道“唯一不變的就是變化”呢?然而在這一點(diǎn)上,說到和做到是有距離的。
真想做到模型的演進(jìn),不僅需要上述《DDD》中的建模技能,還要扎實(shí)地掌握重構(gòu)、TDD(或者至少是自動化測試)和持續(xù)集成,我將之稱為敏捷工程實(shí)踐的“老三樣”。此外,還要掌握架構(gòu)演進(jìn)和數(shù)據(jù)庫演進(jìn)的若干種模式,以及建立多維度的指標(biāo)體系,利用工具進(jìn)行量化的架構(gòu)守護(hù)等。
然而我們很多人,哪怕接受了DDD,也是急于學(xué)習(xí)建模技能(這本身沒有錯),而對模型演進(jìn)絲毫不感興趣。這樣的DDD往往半途而廢。這是因?yàn)椋绻荒苎葸M(jìn)模型(進(jìn)而演進(jìn)整個軟件系統(tǒng)),那么軟件和模型就會逐漸不一致,最后模型變成廢紙一張,一切打回原形。這背后可以回歸到團(tuán)隊甚至企業(yè)文化的問題。衡量一個企業(yè)的文化,關(guān)鍵不是看怎么說,而是看怎么做。
談完“動態(tài)”,再談“內(nèi)因驅(qū)動”。
《矛盾論》中強(qiáng)調(diào)“唯物辯證法認(rèn)為外因是變化的條件,內(nèi)因是變化的根據(jù),外因通過內(nèi)因而起作用”。內(nèi)因是第一位的,外因是第二位的。而形而上學(xué)只看到外因,外因也能使事物發(fā)生變化,但只是量的變化。而內(nèi)因才能導(dǎo)致質(zhì)的變化。換句話說,事物本身必然包含內(nèi)在的矛盾,這一矛盾的發(fā)展變化,在一定外因的條件下,會導(dǎo)致事物發(fā)生質(zhì)的變化。
聯(lián)系到模型的演進(jìn),我們從《DDD》中的相關(guān)例子看到,隨著對領(lǐng)域知識理解的深入,模型的重構(gòu)往往不是多了幾個實(shí)體、少了幾個關(guān)聯(lián),而是多出了若干抽象層次,甚至將模型的核心部分打碎重組。所以我們既要對這一點(diǎn)有充分的心里準(zhǔn)備,知道這是復(fù)雜系統(tǒng)的模型演進(jìn)中必然發(fā)生的;也要未雨綢繆,掌握相關(guān)的建模和架構(gòu)演進(jìn)技術(shù)。不過也不用怕,當(dāng)掌握相關(guān)技能后,再復(fù)雜的重構(gòu),總可以小步快跑,穩(wěn)健推進(jìn)。
前面反復(fù)提到了架構(gòu)演進(jìn)。其實(shí)《DDD》和《演進(jìn)式架構(gòu)》是兩本書。兩者的側(cè)重點(diǎn)不同,一本側(cè)重領(lǐng)域建模,一本側(cè)重系統(tǒng)架構(gòu)演進(jìn)。不過在實(shí)踐中我們常常將兩者結(jié)合起來運(yùn)用。下面聊兩句演進(jìn)式架構(gòu)的原理,這超出了《DDD》原書的范圍。
“演進(jìn)式架構(gòu)”強(qiáng)調(diào)增量地、多維度地、導(dǎo)向性地架構(gòu)變化過程。我們主要解釋這三個關(guān)鍵詞。
《演進(jìn)式架構(gòu)》(Building Evolutionary Architectures)和“進(jìn)化論”(Theory of Evolution)中的“演進(jìn)”和“進(jìn)化”其實(shí)是同一個英文詞根。有人將“Theroy of Evolution”翻譯成“進(jìn)化論”,有人翻譯成“演化論”?!堆葸M(jìn)式架構(gòu)》的譯者討了個巧,各取一個字,就成了“演進(jìn)”,其實(shí)都是一個意思。所以,演進(jìn)式架構(gòu)實(shí)際上引用了進(jìn)化論的隱喻。
達(dá)爾文的《物種起源》將進(jìn)化論總結(jié)為遺傳、變異和適者生存。該書第一部分首先講的是“人工選擇”,例如對狗和金魚的馴化;后面才推廣到自然選擇。將演進(jìn)式架構(gòu)和進(jìn)化論進(jìn)行比較,可以看到一些有趣的共同點(diǎn)。
將軟件系統(tǒng)類比為生命體。不影響系統(tǒng)架構(gòu)的漸進(jìn)式修改相當(dāng)于生物的“遺傳”。對架構(gòu)的本質(zhì)性修改相當(dāng)于生物的“變異”。變異的系統(tǒng)需要經(jīng)過各種驗(yàn)證,看看是否符合人的需求,這實(shí)際上是一種“人工選擇”的過程。適應(yīng)需求的架構(gòu)得以延續(xù),不適應(yīng)的則會被淘汰或進(jìn)一步變異,這就是演進(jìn)式架構(gòu)中說的“導(dǎo)向性”,也這就是“適者生存”。達(dá)爾文指出物種的進(jìn)化總是漸進(jìn)式的,而不會短時間內(nèi)發(fā)生劇烈變化。這就是演進(jìn)式架構(gòu)中的“增量式”變化。生物適應(yīng)環(huán)境時,要適應(yīng)多方面的因素:捕食能力、逃避天敵的能力、適應(yīng)氣候的能力、抵御病蟲害的能力、繁殖能力等等。各種因素都適應(yīng),才能生存。同樣,系統(tǒng)也要適應(yīng)多方面的因素:功能的正確性、可擴(kuò)展性、可維護(hù)性、性能、安全等等。這就是演進(jìn)式架構(gòu)中說的“多維度”。
上述類比可以促使我們對演進(jìn)式架構(gòu)進(jìn)行更深入的思考。至于具體技術(shù),可以參閱原書。
限界上下文與人類認(rèn)識能力的有限性
雖然哲學(xué)家可能是人類有史以來最喜歡爭辯的群體,但有一個觀點(diǎn),多數(shù)哲學(xué)家卻是有共識的:人的認(rèn)識能力是有限的。從孟子的“吾生也有涯,而知也無涯”,到康德的“物自體不可知”,再到辯證唯物主義的“人只能認(rèn)識相對真理”,都說明了這一點(diǎn)。那么,這和限界上下文有什么關(guān)系呢?
如果問一位同事限界上下文是什么,常常聽到這樣的回答:“業(yè)務(wù)功能的邊界”“業(yè)務(wù)領(lǐng)域的邊界”諸如此類。這種說法雖然沒錯,但說的是結(jié)果,不是原因;是表象,不是本質(zhì)。因?yàn)榫酆?、模塊等也可以說是一種“業(yè)務(wù)功能的邊界”。所以上述回答沒有答到點(diǎn)子上。
限界上下文是在《DDD》第14章“保持模型的完整性”中介紹的。眼尖的同學(xué)可能就會問了:為什么這一章的名字不叫做“劃分功能邊界”?這里的“完整性”到底指什么?
讓我們繼續(xù)看原文。這一章的開頭幾段所舉的例子,是說有兩個團(tuán)隊共用了一個Charge(收費(fèi))對象。但兩個團(tuán)隊所說的Charge背后的業(yè)務(wù)概念其實(shí)是有差別的。開始時兩個團(tuán)隊都沒有意識到這個問題。他們都去改這個類,后來程序崩潰了。然后作者說“模型最基本的要求是它應(yīng)該保持內(nèi)部一致,術(shù)語總具有相同的意義,并且不包含互相矛盾的規(guī)則……”。所以,作者在本章要解決的實(shí)際上是系統(tǒng)的一致性問題。
這一章標(biāo)題中的“完整性”是對單詞Integrity的翻譯。Integrity固然有“完整性”的意思,但也包含“統(tǒng)一”、“健全”等含義。因此,本章的標(biāo)題如果翻譯成“保持模型的統(tǒng)一”會更準(zhǔn)確。翻譯成“完整性”則容易造成誤導(dǎo)。
保持系統(tǒng)的一致性,早已被IT界的前輩們所重視。例如,Brooks老師在七十年代寫的《人月神話》中已經(jīng)做了強(qiáng)調(diào)。不過,之前的專家們在潛意識里總是認(rèn)為,對于一個復(fù)雜的系統(tǒng),總有辦法做到團(tuán)隊理解的全局一致性?!度嗽律裨挕分芯土信e了若干種方法。
然而《DDD》中指出“在理想世界中,我們可以得到涵蓋整個企業(yè)領(lǐng)域的單一模型。這個模型將是統(tǒng)一的,沒有任何相互矛盾或相互重疊的術(shù)語定義”然而“大型系統(tǒng)領(lǐng)域模型的完全統(tǒng)一既不可行,也不劃算”。
為什么“不可行”呢?答案就是人類認(rèn)識能力的有限性。由于大型軟件是團(tuán)隊協(xié)作開發(fā)的,因此這里的認(rèn)識能力還要擴(kuò)展到群體的認(rèn)識能力。也就是包含了個人的認(rèn)識能力、群體的協(xié)作能力、以及兩者的相互作用。越是復(fù)雜的系統(tǒng),認(rèn)知起來越困難。當(dāng)系統(tǒng)復(fù)雜性達(dá)到一定程度時,就超過了一個團(tuán)隊的認(rèn)知能力,無法保證系統(tǒng)的一致性了。
解決的辦法就是分而治之,將一個“理想中”的大模型劃分成幾個小模型,每個小模型不超過團(tuán)隊的認(rèn)知能力。因此每個小模型內(nèi)部就可以保證嚴(yán)格的一致性,而與另一個模型內(nèi)部不需要一致,只需要約定好模型之間的接口就可以了。也就是說,我們在了解了人類認(rèn)識能力的有限性之后,不再追求全局一致性,而是退而求其次,代之以局部的一致性,從而使系統(tǒng)的正確性在總體上得到管理,實(shí)現(xiàn)業(yè)務(wù)目標(biāo),這樣就足夠了。
概念的一致性是通過語義上的一致性來表達(dá)的,所以作者引用了語言學(xué)上“上下文”的術(shù)語。在日常語言中,人們常常會隨時切換上下文,在特定場景下未必會影響溝通。但在計算機(jī)軟件中,一個元素要么屬于一個上下文,要么不屬于,這個界限必須清清楚楚。為了強(qiáng)調(diào)這一邊界的重要性,因此稱為“限界上下文”。
上下文的具體劃分方法,仍然要圍繞領(lǐng)域概念的內(nèi)聚和耦合關(guān)系。因此劃分的結(jié)果,當(dāng)然表現(xiàn)了一個“領(lǐng)域的邊界”或者說“功能的邊界”。如上文所述,這是結(jié)果而不是原因。
根據(jù)敏捷組織的理論,一個開發(fā)組的規(guī)模最好在5~9人之間(這是有心理學(xué)研究作為依據(jù)的)。那么,一個限界上下文的規(guī)模,一般就是這樣一個開發(fā)組所能把握的規(guī)模。
知道了人類認(rèn)識能力的局限,就使我們對這個世界秉持謙卑和敬畏的態(tài)度。這也有助于我們理解限界上下文所要解決的問題、劃分的方法、及其與團(tuán)隊的關(guān)系。最終使我們能夠正確劃分上下文,管控好復(fù)雜系統(tǒng)的一致性。
在這個系列文章的最后一篇,我們將繼續(xù)討論“核心域”、“統(tǒng)一語言”,并對全文做一個總結(jié)。
原文鏈接:??DDD的哲學(xué):模型的關(guān)聯(lián)、演進(jìn)和認(rèn)知 - Thoughtworks洞見??