新聞中心
邊界通過限界上下文來確定,這在領(lǐng)域驅(qū)動設(shè)計中具有非凡的意義。對應(yīng)于通用語言,限界上下文是語言的邊界,對于領(lǐng)域模型,限界上下文是模型的邊界,二者對應(yīng)于問題空間(Problem Space)的界定。對于系統(tǒng)的架構(gòu),限界上下文還確定了應(yīng)用邊界和技術(shù)邊界,進而幫助我們確定整個系統(tǒng)及各個限界上下文的解決方案。可以說,限界上下文是連接問題空間與解決方案空間的重要橋梁。

銀州網(wǎng)站制作公司哪家好,找成都創(chuàng)新互聯(lián)公司!從網(wǎng)頁設(shè)計、網(wǎng)站建設(shè)、微信開發(fā)、APP開發(fā)、響應(yīng)式網(wǎng)站等網(wǎng)站項目制作,到程序開發(fā),運營維護。成都創(chuàng)新互聯(lián)公司于2013年創(chuàng)立到現(xiàn)在10年的時間,我們擁有了豐富的建站經(jīng)驗和運維經(jīng)驗,來保證我們的工作的順利進行。專注于網(wǎng)站建設(shè)就選成都創(chuàng)新互聯(lián)公司。
那么,限界上下文所界定的邊界,究竟是邏輯邊界,還是物理邊界?這并沒有定論,需得依據(jù)不同場景而做出不同的決策。
邏輯邊界
根據(jù)業(yè)務(wù)對領(lǐng)域進行邏輯分解時,分與合是兩個矛盾而又統(tǒng)一的概念。合是目標,分是降低復(fù)雜度的一種手段。分實則是為了更好的合。通過業(yè)務(wù)分解,每個分解出來的限界上下文規(guī)模就變得更小,因而更容易理解和把控。由于這種分解是從業(yè)務(wù)相關(guān)性來考慮的,使得領(lǐng)域可以更加細分,業(yè)務(wù)分析師或者領(lǐng)域?qū)<揖涂梢灾灰笳莆崭蛹毞值膶>I(lǐng)域。
從系統(tǒng)的代碼模型(Code Model)看,所謂邏輯邊界有兩種表現(xiàn)形式。以Java為例,歸納如下:
- 命名空間級別:邏輯邊界僅僅通過命名空間進行界定,但是所有的限界上下文其實都處于同一個模塊中,編譯后都屬于同一個Jar包。
- 模塊級別:在命名空間上是邏輯分離的,而不同限界上下文則屬于同一個項目的不同模塊,編譯后會生成各自的Jar包。若限界上下文之間存在依賴,則在運行時,這些Jar會被同時加載到同一個Java虛擬機中。這里所謂的“模塊”,在Java代碼中也可以創(chuàng)建為Jigsaw的module。
將限定上下文的邊界視為邏輯邊界是最常見也是最簡單的一種形式。一方面邏輯的分離可以保證系統(tǒng)代碼的清晰結(jié)構(gòu),另一方面它也使得限界上下文之間的協(xié)作變得更加容易,更加高效。在物理上,限界上下文彼此之間的通信其實是無縫集成的,要重用的領(lǐng)域模型都可以直接訪問,并對模型類進行實例化。如下是國際報稅系統(tǒng)的邏輯邊界(Java):
然而,正所謂越容易重用,就越容易產(chǎn)生耦合。編寫代碼時,我們需要謹守這條無形的邏輯邊界,時刻注意不要逾界,并確定限界上下文各自對外公開的接口,避免對具體的實現(xiàn)產(chǎn)生依賴。
采用邏輯邊界劃分限界上下文的系統(tǒng)架構(gòu)是單塊(Monolithic)架構(gòu),所有的限界上下文都部署在同一個進程中,因此不能針對某一個限界上下文進行水平伸縮。需要對限界上下文的實現(xiàn)進行替換或升級時,會影響到整個系統(tǒng)。即使我們守住了邏輯邊界,這種耦合仍然存在,導(dǎo)致各個限界上下文的開發(fā)互相影響,團隊之間的協(xié)調(diào)成本也隨之而增加。
物理邊界
邏輯邊界的壞,正是物理邊界的好;反過來,物理邊界的壞,同樣是邏輯邊界的好。當我們將限界上下文的邊界定義為物理邊界時,每個限界上下文就變成了一個個細粒度的微服務(wù)。
這里,我們需要針對Eric Evans提出的“限界上下文”概念做進一步澄清:限界上下文究竟是僅僅針對領(lǐng)域模型的邊界劃分,還是對整個架構(gòu)(包括基礎(chǔ)設(shè)施層以及需要使用的外部資源)垂直方向的劃分?正如前面對Eric Evans觀點的引用,他在《領(lǐng)域驅(qū)動設(shè)計》一書中明確地指出:“根據(jù)團隊的組織、軟件系統(tǒng)的各個部分的用法以及物理表現(xiàn)(代碼和數(shù)據(jù)庫模式等)來設(shè)置模型的邊界。”顯然,限界上下文不僅僅作用于領(lǐng)域?qū)雍蛻?yīng)用層。它是架構(gòu)設(shè)計而非僅僅是領(lǐng)域設(shè)計的關(guān)鍵因素。
倘若我們將限界上下文的邊界視為物理邊界,則可以保證邊界內(nèi)的服務(wù)、基礎(chǔ)設(shè)施乃至于存儲資源、中間件等其他外部資源的完整性,最終形成自治的服務(wù)。限界上下文之間僅僅通過限定的方式以限定的通信協(xié)議和數(shù)據(jù)格式進行通信,除此之外,彼此沒有任何共享,這種架構(gòu)被稱之為零共享架構(gòu)。這種架構(gòu)的表現(xiàn)形式為:每個限界上下文都有自己的代碼庫、數(shù)據(jù)存儲以及開發(fā)團隊,每個限界上下文選擇的技術(shù)棧和語言平臺也可以不同。當每個限界上下文都被物理隔離時,一個限界上下文的開發(fā)人員就不能調(diào)用另一個限界上下文的方法,或者將數(shù)據(jù)存儲在共享結(jié)構(gòu)中了,這可以避免因為共享帶來的耦合。下圖為危機分析系統(tǒng)的架構(gòu):
物理分隔開的限界上下文變得小而專,使得我們可以很好地安排遵循2PTs規(guī)則的小團隊去治理它。然而,這種架構(gòu)的復(fù)雜度也不可低估。限界上下文之間的通信是跨進程的,我們需要考慮通信的健壯性。數(shù)據(jù)庫是完全分離的,當需要關(guān)聯(lián)之間的數(shù)據(jù)時,需得跨限界上下文去訪問,無法享受數(shù)據(jù)庫自身提供的關(guān)聯(lián)福利。由于每個限界上下文都是分布式的,如何保證數(shù)據(jù)的一致性也是一件棘手的問題。當整個系統(tǒng)都被分解成一個個可以獨立部署的限界上下文時,運維與監(jiān)控的復(fù)雜度也隨之而劇增。
數(shù)據(jù)庫共享
在邏輯邊界和物理邊界中間,還存在一種折中的手段。在考慮限界上下文劃分時,分開考慮代碼模型與數(shù)據(jù)庫模型,就可能出現(xiàn)在代碼上分離,而在數(shù)據(jù)庫層面卻存在數(shù)據(jù)共享的形式,即多個限界上下文共享同一個數(shù)據(jù)庫。
因為沒有分庫,在數(shù)據(jù)庫層面就可以更好地保證事務(wù)的ACID。這或許是該方案最有說服力的證據(jù),但也可以視為是對“一致性”約束的妥協(xié)。
數(shù)據(jù)庫共享的問題在于數(shù)據(jù)庫的變化方向與業(yè)務(wù)的變化方向會不一致。這種不一致性體現(xiàn)在兩個方面:
- 耦合:雖然業(yè)務(wù)上限界上下文之間是解耦的,但是在數(shù)據(jù)庫層面依然存在強耦合關(guān)系
- 水平伸縮:部署在應(yīng)用服務(wù)器的應(yīng)用服務(wù)可以根據(jù)限界上下文的邊界單獨進行水平伸縮,但是在數(shù)據(jù)庫層面卻無法做到
根據(jù)Netflix團隊提出的微服務(wù)架構(gòu)***實踐,其中一個最重要特征就是“每個微服務(wù)的數(shù)據(jù)單獨存儲”。但是服務(wù)的分離并不絕對代表數(shù)據(jù)應(yīng)該分離。數(shù)據(jù)庫的樣式(Schema)與領(lǐng)域模型未必存在一對一的映射關(guān)系。在對數(shù)據(jù)進行分庫設(shè)計時,如果僅僅站在業(yè)務(wù)邊界的角度去思考,可能會因為分庫的粒度太小,導(dǎo)致不必要的跨庫關(guān)聯(lián)。因此,我們可以將“數(shù)據(jù)庫共享”模式視為一種過渡方案,不要在一開始設(shè)計微服務(wù)的時候,就直接將數(shù)據(jù)徹底分開,而是采用演進式的設(shè)計。
為了便于在演進設(shè)計中將分表重構(gòu)為分庫,從一開始要注意避免在兩個表之間建立外鍵約束關(guān)系。某些關(guān)系型數(shù)據(jù)庫可能通過這種約束關(guān)系提供級聯(lián)更新與刪除的功能,這種功能反過來會影響代碼的實現(xiàn)。一旦因為分庫而去掉表之間的外鍵約束關(guān)系,需要修改的代碼太多,會導(dǎo)致演進的成本太高,甚至可能因為某種疏漏帶來隱藏的Bug。
沒有外鍵約束關(guān)系可能在當前增加了開發(fā)成本,卻為未來的演進打開了方便之門。例如,在針對某手機品牌開發(fā)的輿情分析系統(tǒng)中,危機查詢服務(wù)提供對識別出來的危機的查詢,需要通過userId獲得危機處理人、危機匯報人的詳細信息。左圖為演進前直接通過數(shù)據(jù)庫查詢的方式,右圖則切斷了這種數(shù)據(jù)庫耦合,改為服務(wù)調(diào)用的方式:
倘若架構(gòu)被設(shè)計為數(shù)據(jù)庫共享,且兩個服務(wù)需要操作同一張數(shù)據(jù)表(這張表被稱之為“共享表”),則傳遞了一個信號,即我們的設(shè)計可能出現(xiàn)了錯誤:
- 遺漏了一個限界上下文,共享表對應(yīng)的是一個被重用的服務(wù):買家在查詢商品時,商品服務(wù)會查詢價格表中的當前價格,而在提交訂單時,訂單服務(wù)也會查詢價格表中的價格,計算當前的訂單總額;共享價格數(shù)據(jù)的原因是我們遺漏了價格上下文,通過引入價格服務(wù)就可以解除這種不必要的數(shù)據(jù)共享。
- 職責(zé)分配出現(xiàn)了問題,操作共享表的職責(zé)應(yīng)該分配給已有的服務(wù):輿情服務(wù)與危機服務(wù)都需要從郵件模板表中獲取模板數(shù)據(jù),然后再調(diào)用郵件服務(wù)組合模板的內(nèi)容發(fā)送郵件;實際上從郵件模板表獲取模板數(shù)據(jù)的職責(zé)應(yīng)該分配給已有的郵件服務(wù)。
- 共享表對應(yīng)兩個限界上下文的不同概念:倉儲上下文與訂單上下文都需要訪問共享的產(chǎn)品表,但實際上這兩個上下文需要的產(chǎn)品信息是完全不同的,應(yīng)該按照限界上下文的邊界分開為產(chǎn)品建表。
為什么會出現(xiàn)這三種錯誤的設(shè)計?根本原因還是在于我們沒有通過業(yè)務(wù)建模,而是在數(shù)據(jù)庫中隱式地進行建模,因而在代碼中沒有體現(xiàn)正確的領(lǐng)域模型,從而導(dǎo)致了數(shù)據(jù)庫層面的耦合或共享。
【本文為專欄作者“張逸”原創(chuàng)稿件,轉(zhuǎn)載請聯(lián)系原作者】
文章名稱:限界上下文—邏輯邊界or物理邊界?
網(wǎng)址分享:http://m.5511xx.com/article/ccsgijj.html


咨詢
建站咨詢
