自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

限界上下文—邏輯邊界or物理邊界?

開發(fā) 開發(fā)工具
本文將徹底對限界上下文做一個全方位的解剖,包括解讀限界上下文的定義、價值,了解限界上下文的三種邊界,并提出如何識別限界上下文的方法。

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

那么,限界上下文所界定的邊界,究竟是邏輯邊界,還是物理邊界?這并沒有定論,需得依據(jù)不同場景而做出不同的決策。

邏輯邊界

根據(jù)業(yè)務(wù)對領(lǐng)域進(jìn)行邏輯分解時,分與合是兩個矛盾而又統(tǒng)一的概念。合是目標(biāo),分是降低復(fù)雜度的一種手段。分實(shí)則是為了更好的合。通過業(yè)務(wù)分解,每個分解出來的限界上下文規(guī)模就變得更小,因而更容易理解和把控。由于這種分解是從業(yè)務(wù)相關(guān)性來考慮的,使得領(lǐng)域可以更加細(xì)分,業(yè)務(wù)分析師或者領(lǐng)域?qū)<揖涂梢灾灰笳莆崭蛹?xì)分的專精領(lǐng)域。

從系統(tǒng)的代碼模型(Code Model)看,所謂邏輯邊界有兩種表現(xiàn)形式。以Java為例,歸納如下:

  • 命名空間級別:邏輯邊界僅僅通過命名空間進(jìn)行界定,但是所有的限界上下文其實(shí)都處于同一個模塊中,編譯后都屬于同一個Jar包。
  • 模塊級別:在命名空間上是邏輯分離的,而不同限界上下文則屬于同一個項(xiàng)目的不同模塊,編譯后會生成各自的Jar包。若限界上下文之間存在依賴,則在運(yùn)行時,這些Jar會被同時加載到同一個Java虛擬機(jī)中。這里所謂的“模塊”,在Java代碼中也可以創(chuàng)建為Jigsaw的module。

將限定上下文的邊界視為邏輯邊界是最常見也是最簡單的一種形式。一方面邏輯的分離可以保證系統(tǒng)代碼的清晰結(jié)構(gòu),另一方面它也使得限界上下文之間的協(xié)作變得更加容易,更加高效。在物理上,限界上下文彼此之間的通信其實(shí)是無縫集成的,要重用的領(lǐng)域模型都可以直接訪問,并對模型類進(jìn)行實(shí)例化。如下是國際報(bào)稅系統(tǒng)的邏輯邊界(Java):

國際報(bào)稅系統(tǒng)的邏輯邊界(Java)

然而,正所謂越容易重用,就越容易產(chǎn)生耦合。編寫代碼時,我們需要謹(jǐn)守這條無形的邏輯邊界,時刻注意不要逾界,并確定限界上下文各自對外公開的接口,避免對具體的實(shí)現(xiàn)產(chǎn)生依賴。

采用邏輯邊界劃分限界上下文的系統(tǒng)架構(gòu)是單塊(Monolithic)架構(gòu),所有的限界上下文都部署在同一個進(jìn)程中,因此不能針對某一個限界上下文進(jìn)行水平伸縮。需要對限界上下文的實(shí)現(xiàn)進(jìn)行替換或升級時,會影響到整個系統(tǒng)。即使我們守住了邏輯邊界,這種耦合仍然存在,導(dǎo)致各個限界上下文的開發(fā)互相影響,團(tuán)隊(duì)之間的協(xié)調(diào)成本也隨之而增加。

物理邊界

邏輯邊界的壞,正是物理邊界的好;反過來,物理邊界的壞,同樣是邏輯邊界的好。當(dāng)我們將限界上下文的邊界定義為物理邊界時,每個限界上下文就變成了一個個細(xì)粒度的微服務(wù)。

這里,我們需要針對Eric Evans提出的“限界上下文”概念做進(jìn)一步澄清:限界上下文究竟是僅僅針對領(lǐng)域模型的邊界劃分,還是對整個架構(gòu)(包括基礎(chǔ)設(shè)施層以及需要使用的外部資源)垂直方向的劃分?正如前面對Eric Evans觀點(diǎn)的引用,他在《領(lǐng)域驅(qū)動設(shè)計(jì)》一書中明確地指出:“根據(jù)團(tuán)隊(duì)的組織、軟件系統(tǒng)的各個部分的用法以及物理表現(xiàn)(代碼和數(shù)據(jù)庫模式等)來設(shè)置模型的邊界。”顯然,限界上下文不僅僅作用于領(lǐng)域?qū)雍蛻?yīng)用層。它是架構(gòu)設(shè)計(jì)而非僅僅是領(lǐng)域設(shè)計(jì)的關(guān)鍵因素。

倘若我們將限界上下文的邊界視為物理邊界,則可以保證邊界內(nèi)的服務(wù)、基礎(chǔ)設(shè)施乃至于存儲資源、中間件等其他外部資源的完整性,最終形成自治的服務(wù)。限界上下文之間僅僅通過限定的方式以限定的通信協(xié)議和數(shù)據(jù)格式進(jìn)行通信,除此之外,彼此沒有任何共享,這種架構(gòu)被稱之為零共享架構(gòu)。這種架構(gòu)的表現(xiàn)形式為:每個限界上下文都有自己的代碼庫、數(shù)據(jù)存儲以及開發(fā)團(tuán)隊(duì),每個限界上下文選擇的技術(shù)棧和語言平臺也可以不同。當(dāng)每個限界上下文都被物理隔離時,一個限界上下文的開發(fā)人員就不能調(diào)用另一個限界上下文的方法,或者將數(shù)據(jù)存儲在共享結(jié)構(gòu)中了,這可以避免因?yàn)楣蚕韼淼鸟詈稀O聢D為危機(jī)分析系統(tǒng)的架構(gòu):

危機(jī)分析系統(tǒng)的架構(gòu)

物理分隔開的限界上下文變得小而專,使得我們可以很好地安排遵循2PTs規(guī)則的小團(tuán)隊(duì)去治理它。然而,這種架構(gòu)的復(fù)雜度也不可低估。限界上下文之間的通信是跨進(jìn)程的,我們需要考慮通信的健壯性。數(shù)據(jù)庫是完全分離的,當(dāng)需要關(guān)聯(lián)之間的數(shù)據(jù)時,需得跨限界上下文去訪問,無法享受數(shù)據(jù)庫自身提供的關(guān)聯(lián)福利。由于每個限界上下文都是分布式的,如何保證數(shù)據(jù)的一致性也是一件棘手的問題。當(dāng)整個系統(tǒng)都被分解成一個個可以獨(dú)立部署的限界上下文時,運(yùn)維與監(jiān)控的復(fù)雜度也隨之而劇增。

數(shù)據(jù)庫共享

在邏輯邊界和物理邊界中間,還存在一種折中的手段。在考慮限界上下文劃分時,分開考慮代碼模型與數(shù)據(jù)庫模型,就可能出現(xiàn)在代碼上分離,而在數(shù)據(jù)庫層面卻存在數(shù)據(jù)共享的形式,即多個限界上下文共享同一個數(shù)據(jù)庫。

因?yàn)闆]有分庫,在數(shù)據(jù)庫層面就可以更好地保證事務(wù)的ACID。這或許是該方案最有說服力的證據(jù),但也可以視為是對“一致性”約束的妥協(xié)。

數(shù)據(jù)庫共享的問題在于數(shù)據(jù)庫的變化方向與業(yè)務(wù)的變化方向會不一致。這種不一致性體現(xiàn)在兩個方面:

  • 耦合:雖然業(yè)務(wù)上限界上下文之間是解耦的,但是在數(shù)據(jù)庫層面依然存在強(qiáng)耦合關(guān)系
  • 水平伸縮:部署在應(yīng)用服務(wù)器的應(yīng)用服務(wù)可以根據(jù)限界上下文的邊界單獨(dú)進(jìn)行水平伸縮,但是在數(shù)據(jù)庫層面卻無法做到

根據(jù)Netflix團(tuán)隊(duì)提出的微服務(wù)架構(gòu)***實(shí)踐,其中一個最重要特征就是“每個微服務(wù)的數(shù)據(jù)單獨(dú)存儲”。但是服務(wù)的分離并不絕對代表數(shù)據(jù)應(yīng)該分離。數(shù)據(jù)庫的樣式(Schema)與領(lǐng)域模型未必存在一對一的映射關(guān)系。在對數(shù)據(jù)進(jìn)行分庫設(shè)計(jì)時,如果僅僅站在業(yè)務(wù)邊界的角度去思考,可能會因?yàn)榉謳斓牧6忍?,?dǎo)致不必要的跨庫關(guān)聯(lián)。因此,我們可以將“數(shù)據(jù)庫共享”模式視為一種過渡方案,不要在一開始設(shè)計(jì)微服務(wù)的時候,就直接將數(shù)據(jù)徹底分開,而是采用演進(jìn)式的設(shè)計(jì)。

為了便于在演進(jìn)設(shè)計(jì)中將分表重構(gòu)為分庫,從一開始要注意避免在兩個表之間建立外鍵約束關(guān)系。某些關(guān)系型數(shù)據(jù)庫可能通過這種約束關(guān)系提供級聯(lián)更新與刪除的功能,這種功能反過來會影響代碼的實(shí)現(xiàn)。一旦因?yàn)榉謳於サ舯碇g的外鍵約束關(guān)系,需要修改的代碼太多,會導(dǎo)致演進(jìn)的成本太高,甚至可能因?yàn)槟撤N疏漏帶來隱藏的Bug。

沒有外鍵約束關(guān)系可能在當(dāng)前增加了開發(fā)成本,卻為未來的演進(jìn)打開了方便之門。例如,在針對某手機(jī)品牌開發(fā)的輿情分析系統(tǒng)中,危機(jī)查詢服務(wù)提供對識別出來的危機(jī)的查詢,需要通過userId獲得危機(jī)處理人、危機(jī)匯報(bào)人的詳細(xì)信息。左圖為演進(jìn)前直接通過數(shù)據(jù)庫查詢的方式,右圖則切斷了這種數(shù)據(jù)庫耦合,改為服務(wù)調(diào)用的方式:

服務(wù)調(diào)用的方式

倘若架構(gòu)被設(shè)計(jì)為數(shù)據(jù)庫共享,且兩個服務(wù)需要操作同一張數(shù)據(jù)表(這張表被稱之為“共享表”),則傳遞了一個信號,即我們的設(shè)計(jì)可能出現(xiàn)了錯誤:

  • 遺漏了一個限界上下文,共享表對應(yīng)的是一個被重用的服務(wù):買家在查詢商品時,商品服務(wù)會查詢價格表中的當(dāng)前價格,而在提交訂單時,訂單服務(wù)也會查詢價格表中的價格,計(jì)算當(dāng)前的訂單總額;共享價格數(shù)據(jù)的原因是我們遺漏了價格上下文,通過引入價格服務(wù)就可以解除這種不必要的數(shù)據(jù)共享。
  • 職責(zé)分配出現(xiàn)了問題,操作共享表的職責(zé)應(yīng)該分配給已有的服務(wù):輿情服務(wù)與危機(jī)服務(wù)都需要從郵件模板表中獲取模板數(shù)據(jù),然后再調(diào)用郵件服務(wù)組合模板的內(nèi)容發(fā)送郵件;實(shí)際上從郵件模板表獲取模板數(shù)據(jù)的職責(zé)應(yīng)該分配給已有的郵件服務(wù)。
  • 共享表對應(yīng)兩個限界上下文的不同概念:倉儲上下文與訂單上下文都需要訪問共享的產(chǎn)品表,但實(shí)際上這兩個上下文需要的產(chǎn)品信息是完全不同的,應(yīng)該按照限界上下文的邊界分開為產(chǎn)品建表。

為什么會出現(xiàn)這三種錯誤的設(shè)計(jì)?根本原因還是在于我們沒有通過業(yè)務(wù)建模,而是在數(shù)據(jù)庫中隱式地進(jìn)行建模,因而在代碼中沒有體現(xiàn)正確的領(lǐng)域模型,從而導(dǎo)致了數(shù)據(jù)庫層面的耦合或共享。

【本文為51CTO專欄作者“張逸”原創(chuàng)稿件,轉(zhuǎn)載請聯(lián)系原作者】

戳這里,看該作者更多好文

責(zé)任編輯:趙寧寧 來源: 51CTO專欄
相關(guān)推薦

2021-05-09 21:50:48

項(xiàng)目實(shí)踐上下文

2017-05-11 14:00:02

Flask請求上下文應(yīng)用上下文

2012-12-31 10:01:34

SELinuxSELinux安全

2022-09-14 13:13:51

JavaScript上下文

2022-09-15 08:01:14

繼承基礎(chǔ)設(shè)施基礎(chǔ)服務(wù)

2024-09-30 14:10:00

2022-10-28 16:24:33

Context上下文鴻蒙

2025-03-18 08:14:05

2023-07-11 10:02:23

2020-07-24 10:00:00

JavaScript執(zhí)行上下文前端

2021-07-26 07:47:36

Cpu上下文進(jìn)程

2012-07-31 10:07:07

架構(gòu)系統(tǒng)架構(gòu)架構(gòu)設(shè)計(jì)

2022-07-05 08:09:26

領(lǐng)域驅(qū)動設(shè)計(jì)

2010-02-25 17:04:54

WCF實(shí)例上下文

2012-07-30 16:29:40

架構(gòu)架構(gòu)模式.NET

2025-04-07 01:02:00

GoAPI語言

2022-04-24 15:37:26

LinuxCPU

2019-05-06 14:36:48

CPULinux寄存器

2023-12-10 13:37:23

Python編程上下文管理

2023-06-15 15:45:42

自然語言語言模型
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號