簡介
當應用程序逐漸變得龐大和復雜后,很多面向對象建模的方法都達不到非常好的可伸縮性。上下文圖是一種通用目的的技術,作為領域驅動開發大家族的一名成員,它幫助架構師和開發人員管理他們在軟件開發項目中不得不面對的形形色色的復雜性。與其他廣為人知的DDD模式相比,上下文圖可以應用在任何軟件開發的場景中,在開發者進行策略性決策時,為他們提供一個高層視圖,比如是采用全套的DDD實現,還是根據項目的特定條件進行取舍等。
在這篇文章中,我們將探討界限上下文,以及如何在構建上下文圖時應用它們,來支持軟件開發項目中的關鍵決策。
多個模型共存
領域驅動開發花了很大力氣強調一件事,即維護應用程序模型的概念完整性。要做到這一點,需要很多因素:
一種敏捷的流程,確保能從用戶和領域專家那里頻繁地獲得反饋,
確保有若干領域專家在場,并且與他們開展創造性的合作,
(在應用和測試代碼中)維護單一的、可共享的模型,并用通用語言精確地進行定義它,
營造一種開放透明的環境,鼓勵學習與探索。
這些對于營造一個可以讓高質量的設計繁榮發展的環境至關重要。即使是這樣的環境,那些常見的DDD元素,比如實體、值對象、聚合,也會逐漸地形成一個復雜領域模型。所以,如果不對模型的概念完整性進行妥協的話,傳統的DDD方法也不能盲目地應用在一個無限大的領域模型中。
如圖1所示,在DDD中,通用語言的關鍵責任是對模型進行完整性檢查。無論是與領域專家的討論,還是最終的實現代碼,都可以通過使用相同的術語,并將領域知識清晰準確地進行定義,以此來保證團隊中的每位成員可以分享都相同的領域知識和軟件。
圖1. 通用語言應該是用于表達模型的唯一語言。團隊中的每位成員應該對每個特定術語達成一致。這些術語不能有歧義,也不允許在不同角色間進行翻譯。
代碼是表達模型的主要形式。雖然其他一些工件或許也能捕獲需求或者局部設計,但是只有代碼自身才會與應用程序的行為永遠保持一致。不過這種看上去美妙的建模方式其實非常脆弱:如果滿足了前面提到的四條要求,它能做到,但是不能被無限地擴展。真正讓模型得以最大程度地擴展,并且不必犧牲其概念完整性的方法叫做“上下文”。
了解“界限上下文”
在領域驅動設計的世界里,“上下文”是這樣定義的:
“一個單詞或一個句子所出現的環境,這個環境會反過來影響它們的含義。”
這段定義初看起來相當含糊。它并沒有直接告訴我們“上下文”的大小、形狀或者其他什么特性。但是最后我們又發現這個定義其實非常準確地描述了“上下文”是什么,如果要想窺得其全貌的話,大概還需要一些具體的例子。
示例1:術語相同,含義不同
第一個例子很簡單,它示范了在術語層面出現歧義的情況。有些詞匯根據不同的使用場景,會有不同的含義。
假設我們正在開發一個基于Web的個人金融管理程序(PFM)。該程序可能用于管理銀行帳戶(Account)、股票、儲蓄,未來可能用于追蹤預算和開銷記錄等等。
在這個程序中,領域術語“帳戶”可能是指不同的概念。談論銀行的時候,一個“帳戶”在邏輯上其實是“金錢的容器”;于是我們自然而然地為相應的類加上“余額”、“帳戶號”等屬性。但是,在“Web應用程序”的上下文中,術語“帳戶”會有非常不同的含義,它和用戶的認證、可信度有關。如圖2所示,相應的模型將是完全不同的。
圖2. 一個出現歧義的簡單場景:當術語“帳戶”應用在不同的上下文時,它的含義可以是完全不同的。
這應該是我們在對應用程序建模的時候,所遇到的最簡單的歧義場景了:一個術語,有兩個與上下文相關的含義。這個問題的解決辦法通常是在類的全名(類名或者包名)前面加一些前綴,以此來劃分名字空間。但是在概念層面上,必須清楚我們在和兩個上下文打交道,有時不同上下文之間的區別很大,足以防止開發人員犯錯誤,但有時這樣的區別卻非常微妙。
不過,在類名層面上解決問題的方式并不能用在所有的情況下:在銀行的領域里,術語“銀行帳戶”或許已經存在了,但卻有不同的含義;或者領域專家堅持使用“帳戶”作為術語。此時切記不要發明一個特殊的兩頭這種的術語,或者在專家術語和代碼之間引入一個“翻譯層”。否則你將不得不面對兩個獨立的上下文。
繪制第一份上下文圖
當歧義真的到來的時候,我們需要一種工具來讓開發團隊明白,應用程序中正存在著兩個不同的上下文。“歧義”是通用語言的頭號大敵,我們必須鏟除它。消除歧義的最好辦法就是在上下文圖中,將領域結構分拆在多個界限上下文中。
圖3. 包含兩個領域上下文的上下文圖
按照領域驅動設計一書的描述,上下文圖是用于將上下文邊界變得更清晰的重要工具。其基本的想法是在白板上畫出上下文的邊界,然后選擇性地將相關類的領域術語填寫在上面。它不是一幅繪制精美的UML圖:它是一個可用的工具,允許我們描繪那種模糊不清的狀況,因此它自身看上去模糊不清也就不足為其了。
示例2:概念相同,用法不同
還有一種更加令人困惑的情況,就是底層的概念相同,但是使用的方式不同,最終導致了不同的模型。銀行帳戶的模型是一個BankingAccount類,如圖4所示。