你真的正確實現(xiàn)了領域模型嗎?
你的代碼真的正確實現(xiàn)領域模型了嗎?這個題目從領域驅(qū)動設計實踐者的角度來看,多少有些模糊不清了。代碼?領域模型?根據(jù)Eric Evans的《Domain-Driven Design》一書,代碼本身不也是一種領域模型嗎?在開始本篇正題之前,有必要先對相關概念做簡單梳理。
Eric Evans認為,領域模型本身并不是某種特殊的圖,而是這種圖所要傳達的知識,并且這還特指針對領域?qū)<翌^腦中的知識的一種嚴格的組織且有選擇的抽象。
根據(jù)這種理解,領域模型可以被自由地表示為圖、文字甚至是代碼!也就是說,領域模型和代碼在Evans看來本質(zhì)上是同一種東西。
盡管如此,本文還是使用“領域模型”作為一個與“代碼”相對的概念,這是為什么呢?
這是因為,這種理解常常更符合開發(fā)人員的認知。從大多數(shù)開發(fā)人員的角度來看,“模型”這個概念,不管是否是面向“領域”的,常常區(qū)別于代碼并且以圖的形式存在(比如,典型的UML圖)。實際上,這兩種觀點并不沖突。筆者在這里將前者Evans對領域模型的理解稱為一種“廣義的領域模型”,將后者特指圖形式的領域模型稱為一種“狹義的領域模型”。廣義的與狹義的領域模型的關系如下圖所示。
細心的讀者將會發(fā)現(xiàn),上圖還傳遞了另外兩個觀點。
第一:圖形式的領域模型通常對應于設計階段,而代碼形式的領域模型則通常對應于實現(xiàn)階段。這種觀點其實與張逸老師的《我對領域模型的理解》一文不謀而合。他認為,設計對領域模型的反映,就是設計模型;代碼對領域模型的表達,就是實現(xiàn)模型。在設計階段,軟件設計人員需要基于對領域的理解建立對領域問題的解決方案;在實現(xiàn)階段,開發(fā)人員則根據(jù)設計模型進行編碼實現(xiàn),使領域模型躍然于代碼上。當然,在設計與實現(xiàn)之前還需要有分析階段,領域?qū)<遗c開發(fā)團隊圍繞領域梳理對業(yè)務知識的理解。不過,分析階段關注的又是另外一些問題了,本文主要討論設計與實現(xiàn)階段及其軟件制品之間的關系。
第二:狹義的領域模型與代碼,同時作為廣義的領域模型的特定表現(xiàn)形式,二者之間需要有明確的對應關系。也就是說,代碼應該忠實地反映領域模型,(狹義的)領域模型也應該及時地表現(xiàn)出代碼的變化。正是領域模型與代碼實現(xiàn)之間的緊密聯(lián)系才使模型變得有用,并確保我們所作出的深層次的設計能夠最終轉化為可運行的軟件。當然,如果團隊本身能力很強,完全只使用代碼來表示領域模型,那就可以從根本上避免代碼與領域模型不一致的問題。
言歸正傳,那么,所謂的領域模型和代碼之間“明確的對應關系”具體指的是什么呢?筆者認為,這具體包含三個方面的對應關系:
術語方面,也就是說代碼中使用的領域術語應與(狹義的)領域模型中的一致,這與Evans強調(diào)的在代碼中貫徹使用通用語言(Ubiquitous Language)是一致的。
領域概念的分解,也就是說結構方面的設計,包括領域?qū)ο蠹捌潢P系,在代碼與領域模型中應是一致的。比如,UML類圖與代碼結構之間應該存在明確的對應。
領域概念之間的交互,也就是說行為方面的設計,包括領域?qū)ο笾g的動作,在代碼與領域模型中應是一致的。比如,UML時序圖與代碼行為之間應該存在明確的對應。
為了實現(xiàn)設計階段的領域模型與實現(xiàn)階段的代碼之間的一致,從模型驅(qū)動工程(Model-Driven Engineering)的角度來看讀者可以應用以下幾種策略。
1、從模型到代碼的轉換
從模型到代碼的轉換(Model-to-Code Transformation)是模型驅(qū)動工程的一種典型技術,尤其活躍于2010年前后。將這種技術應用于領域驅(qū)動設計,可以幫助從領域模型自動化地生成代碼,從而減少開發(fā)人員手動編寫代碼的工作量,一定程度上能夠降低由于開發(fā)人員的疏忽造成的與領域模型的不一致。另一方面,由于其自動化的代碼生成過程,該技術還被期望能夠提高軟件開發(fā)效率。
領域驅(qū)動設計社區(qū)推崇的「模型到代碼轉換策略的工具」包括SCULPTOR (http://sculptorgenerator.org/), LEMMA(https://github.com/SeelabFhdo/lemma)等。
然而,從模型到代碼的轉換方法存在兩個固有問題。
其一,由于模型本身相比于代碼總是不夠具體的,因此從模型到代碼的轉換通常只能生成代碼框架,其代碼實現(xiàn)細節(jié)仍需由開發(fā)人員手動實現(xiàn)。這一過程仍然可能造成代碼與領域模型的偏離。
其二,在開發(fā)人員基于生成的代碼框架實現(xiàn)代碼細節(jié)時,領域模型本身可能也在演化,那么,演化后的領域模型所生成的代碼應該如何與開發(fā)人員正在修改的代碼進行合并呢?二者之間的合并沖突問題又應該如何解決?
2、代碼到模型的轉換
從代碼到模型的轉換(Code-to-Model Transformation),是從模型到代碼轉換的逆向過程,可以被認為是模型驅(qū)動的逆向工程(Model-Driven Reverse Engineering)的典型技術。
將這種技術應用于領域驅(qū)動設計,可以幫助從代碼中恢復出可視化的領域模型,所恢復的領域模型本身代表了“在代碼中被實現(xiàn)的領域模型”,同時它也是對代碼進行的一種抽象與可視化。利用恢復得到的圖形式的領域模型,開發(fā)人員能夠進一步對比設計的領域模型與實現(xiàn)的領域模型,從而快速發(fā)現(xiàn)和報告兩者之間的分歧。
更進一步地,還可以在設計的領域模型與實現(xiàn)的領域模型之間進行反射建模(Reflexion Modeling),即通過可視化的映射模型表現(xiàn)出實現(xiàn)的領域模型相比于設計階段的差異(包括新增、刪除與修改元素),從而更直觀地幫助開發(fā)人員識別代碼是否偏離了領域模型。
此外,抽象與可視化后的代碼視圖還可以幫助開發(fā)人員進行知識消化(Knowledge Crunching),減少該過程對代碼實現(xiàn)細節(jié)的依賴,從而降低該過程的認知復雜度。該策略的具體實施方法可以參考筆者最近在軟件學報上發(fā)表的《一種面向領域驅(qū)動設計的逆向建模支持方法》一文(http://www.jos.org.cn/jos/article/abstract/6278?st=search)。
3、模型組合
模型組合(Model Composition)基于關注點分離的思想將軟件系統(tǒng)劃分為模型單元并通過合成處理技術來組裝這些單元以解決系統(tǒng)的復雜性。
將這種技術應用于領域驅(qū)動設計,可以通過組合領域模型元素(及其代碼)得到開發(fā)人員預期的領域模型與代碼,從而降低開發(fā)人員手動實現(xiàn)代碼的工作量,減小代碼偏離領域模型。
這種策略,在筆者看來與目前大行其道的低代碼類似,其本質(zhì)上都通過組件化以及模型驅(qū)動工程等思想來減少開發(fā)人員的代碼編寫。目前,根據(jù)最新的學術文獻綜述(https://www.sciencedirect.com/science/article/pii/S0950584920300689),這種策略仍存在許多挑戰(zhàn),比如如何自動化地匹配要組合的模型元素,缺少工業(yè)規(guī)模的解決方案等。
4、模型與代碼的可追溯性管理
模型驅(qū)動工程被廣泛地用于多個軟件制品之間的可追溯性管理,比如設計文檔、源代碼以及測試用例等。將這種技術應用于領域驅(qū)動設計,可以在領域模型和代碼之間建立鏈接,并通過跟蹤這些鏈接可視化地展示與維護領域模型和代碼之間的一致性。
此外,如果輔以自動化的模型轉換技術(比如從模型到代碼轉換),利用這種機制還可以自動化地維護領域模型和代碼之間的鏈接。也就是說,當源模型(比如領域模型)被修改時,這種修改可以被自動化地傳播到目標模型(比如代碼)上。
缺乏適當?shù)目勺匪菪孕畔⒌拇鎯?、處理與查詢技術被學術界(https://www.sciencedirect.com/science/article/pii/S0950584912001346)認為是這種策略目前存在的局限性。筆者尚未看到這種策略在領域驅(qū)動設計社區(qū)的應用。
更重要的是,以上基于模型驅(qū)動工程的方法實際上都還依賴于某種領域特定語言(Domain Specific Language)。
比如,要想實現(xiàn)自動化的模型到代碼的轉換,期望的領域特定語言需要回答三個問題,即如何表示領域模型,如何表示代碼,以及如何建立領域模型元素到代碼元素之間的映射。
比如,如何在圖形式的領域模型中表示聚合?如何在代碼中表示聚合?如何在這兩種形式的聚合之間建立關聯(lián)?
既然如此,使用這些領域特定語言就很可能會對開發(fā)團隊的建模語言與編程語言等做出一定限制,要想在工業(yè)界落地這些模型驅(qū)動工程方法仍然有很多問題值得被討論。筆者在此列出的一二三不過是班門弄斧罷了。
本文作者是南京大學鐘陳星博士,目前正從事領域驅(qū)動設計與微服務相關的學術研究。此研究對于領域驅(qū)動設計的工程實踐而言,具有較高價值。作為國內(nèi)目前唯一開展領域驅(qū)動設計方向?qū)W術研究的團隊,鐘博士在南京大學張賀教授的指導下,研讀了大量領域驅(qū)動設計的書籍與論文,對這一領域具有頗深造詣。感謝鐘博士的信任,邀請我加入該項目,使我能附驥參與此項研究,以貢獻我在工程實踐方面的一隅之見。