作者 | 陳璐
在軟件開(kāi)發(fā)領(lǐng)域,解耦這個(gè)詞相信大家都不陌生。在面向?qū)ο蟮恼Z(yǔ)境下,我們會(huì)應(yīng)用SOLID原則來(lái)構(gòu)建高內(nèi)聚低耦合的應(yīng)用,實(shí)現(xiàn)模塊間的解耦;在復(fù)雜業(yè)務(wù)系統(tǒng)分析和建模時(shí),會(huì)通過(guò)DDD的戰(zhàn)略和戰(zhàn)術(shù)設(shè)計(jì)幫助劃分領(lǐng)域并實(shí)現(xiàn)分布式系統(tǒng)中服務(wù)的解耦;當(dāng)我們?cè)诮M織大型敏捷開(kāi)發(fā)團(tuán)隊(duì)協(xié)同工作時(shí),通過(guò)組建自治團(tuán)隊(duì)來(lái)減少摩擦,從而實(shí)現(xiàn)團(tuán)隊(duì)級(jí)別的解耦。
可以看到解耦無(wú)處不在,并且以此為目的投入,大家都會(huì)覺(jué)得是無(wú)比的政治正確,因?yàn)閷?shí)現(xiàn)了解耦,我們的系統(tǒng)和應(yīng)用就能更快速的擴(kuò)展和演進(jìn),我們的團(tuán)隊(duì)就能更順暢的合作并能更加快速的實(shí)現(xiàn)業(yè)務(wù)價(jià)值。
但是,當(dāng)我們暫時(shí)拋開(kāi)將得到的種種好處,思考要如何去實(shí)現(xiàn)它時(shí),卻發(fā)現(xiàn)解耦這個(gè)詞表達(dá)的意義過(guò)于抽象和模糊,它既沒(méi)有描述最終的狀態(tài)也沒(méi)有提供實(shí)現(xiàn)的方法。那當(dāng)我們談解耦的時(shí)候,具體內(nèi)容是什么呢?
從字面上理解的所謂耦合,通常是指兩個(gè)或兩個(gè)以上的物體或者體系之間相互作用彼此影響,對(duì)應(yīng)到軟件研發(fā)的以上場(chǎng)景,我們可以轉(zhuǎn)換成是指兩個(gè)或兩個(gè)以上的模塊/系統(tǒng)/團(tuán)隊(duì)之間相互作用彼此影響。
在軟件需要解決的業(yè)務(wù)問(wèn)題越來(lái)復(fù)雜的今天,單個(gè)的系統(tǒng)或者團(tuán)隊(duì)很難在不依賴(lài)外部的情況下去實(shí)現(xiàn)業(yè)務(wù)目標(biāo),所以我理解的解耦并不是要消除耦合(彼此的作用和影響/依賴(lài)),而是指我們應(yīng)該如何通過(guò)一定的方式和規(guī)則,來(lái)設(shè)計(jì)和管理以上提到的多個(gè)元素之間的依賴(lài),降低耦合程度來(lái)使整個(gè)系統(tǒng)有序順暢的運(yùn)轉(zhuǎn)。
本文將從服務(wù)間上下游的思維來(lái)討論如何在系統(tǒng)架構(gòu)演進(jìn)過(guò)程中,持續(xù)的保持服務(wù)間的松耦合,實(shí)現(xiàn)解耦的目標(biāo)。
上下游思維定義
關(guān)于服務(wù)的上下游的定義,在DDD建模方法中,在確定了限界上下文(bounded context)后通過(guò)在上下文映射(context mapping)中使用上下游來(lái)表示上下文依賴(lài)的方向,其確定的依據(jù)是下游需要了解上游的領(lǐng)域知識(shí)實(shí)現(xiàn)業(yè)務(wù),反之則不會(huì)。引申的含義就是上游的業(yè)務(wù)能力可以不用關(guān)心下游業(yè)務(wù)的存在,下游業(yè)務(wù)的開(kāi)展依賴(lài)于上游提供的業(yè)務(wù)能力。下圖是限界上下文映射的一個(gè)例子:
圖片出處:https://www.oreilly.com/library/view/what-is-domain-driven/9781492057802/ch04.html
當(dāng)我們基于以上的限界上下文設(shè)計(jì)領(lǐng)域模型并落地時(shí),理想的情況是一個(gè)限界上下文對(duì)應(yīng)一個(gè)應(yīng)用服務(wù)。參考限界上下文的上下游關(guān)系,我把上下游思維定義為:上游服務(wù)不受下游服務(wù)的業(yè)務(wù)能力和可用性影響,反之則相反。我們會(huì)發(fā)現(xiàn)服務(wù)間的上下游關(guān)系比限界上下文中領(lǐng)域知識(shí)的上下游關(guān)系更復(fù)雜,而且上下游關(guān)系也會(huì)隨著集成方式的不同而變化。
基于上下游思維的耦合級(jí)別
基于服務(wù)上下游的思維,我把服務(wù)間依賴(lài)按以下維度進(jìn)行耦合度分級(jí):
- Level4: 領(lǐng)域知識(shí)互為上下游,業(yè)務(wù)可用性互為上下游
- Level3: 領(lǐng)域知識(shí)互為上下游, 業(yè)務(wù)可用性為單向上下游
- Level2: 領(lǐng)域知識(shí)為單向上下游,業(yè)務(wù)可用性互為上下游
- Level1: 領(lǐng)域知識(shí)為單向上下游,業(yè)務(wù)可用性為單向上下游
由于松耦合的業(yè)務(wù)模型利于松耦合的架構(gòu)設(shè)計(jì)和業(yè)務(wù)的演進(jìn),同時(shí)松耦合的架構(gòu)也利于組建松耦合的團(tuán)隊(duì)結(jié)構(gòu)。業(yè)務(wù)模型作為松耦合設(shè)計(jì)的基礎(chǔ),以上的級(jí)別依據(jù)于這個(gè)思路定義的。
一種常見(jiàn)的Level4級(jí)別的情況是處于伙伴關(guān)系的上下文。比如訂單服務(wù)與派送服務(wù)之間通過(guò)同步API的方式進(jìn)行通信,用戶訂單下單成功,通知派送服務(wù),派送服務(wù)完成,更新訂單狀態(tài)。兩個(gè)服務(wù)通過(guò)API進(jìn)行集成,服務(wù)需要相互知道對(duì)方的部分領(lǐng)域知識(shí)來(lái)完成API的調(diào)用以實(shí)現(xiàn)功能,同時(shí)業(yè)務(wù)的可用性互相關(guān)聯(lián),一方服務(wù)不可用,導(dǎo)致整個(gè)業(yè)務(wù)的中斷。
如果希望耦合度向level3演進(jìn),不希望服務(wù)的可用性產(chǎn)生直接的依賴(lài),我們通常會(huì)通過(guò)引入消息中間件來(lái)進(jìn)行解耦,服務(wù)間通過(guò)消息的方式進(jìn)行集成,由于某些原因,它們都按照對(duì)方的領(lǐng)域模型定義的消息結(jié)構(gòu)進(jìn)行通信。那么這種情況下,服務(wù)間的領(lǐng)域知識(shí)相互耦合,業(yè)務(wù)可用性與具體的服務(wù)解耦,與消息中間件的可用性耦合,我們需要關(guān)注如何提高消息中間件的可用性來(lái)保障業(yè)務(wù)的高可用。
Level2級(jí)別的耦合度是建立在清晰的領(lǐng)域限界上下文邊界基礎(chǔ)上的,在上面包含的訂單服務(wù)和派送服務(wù)的業(yè)務(wù)中,派送服務(wù)作為上游在完成派送進(jìn)行訂單更新這個(gè)業(yè)務(wù)時(shí),它將派送更新的內(nèi)容發(fā)送至訂單服務(wù),訂單服務(wù)再解析派送更新內(nèi)容并更新關(guān)聯(lián)的訂單狀態(tài)。那么在通過(guò)API的方式進(jìn)行集成時(shí),它們就處于領(lǐng)域知識(shí)的單向上下游和業(yè)務(wù)可用性互為上下游的狀態(tài)。具體構(gòu)建服務(wù)時(shí),根據(jù)團(tuán)隊(duì)的組織結(jié)構(gòu)和話語(yǔ)權(quán)的大小,又可以通過(guò)不同的方式來(lái)進(jìn)行服務(wù)的集成。上游服務(wù)通常使用Open Host Service(OHS) / Published Language(PL)來(lái)提供業(yè)務(wù)能力,下游服務(wù)通過(guò)遵循上游的領(lǐng)域模型或者通過(guò)防腐層(Anti Cruption Layer - ACL)來(lái)完成領(lǐng)域模型的轉(zhuǎn)換。處于這個(gè)級(jí)別耦合度的上下游服務(wù)在開(kāi)放主機(jī)接口不變的情況下可以獨(dú)立的進(jìn)行迭代更新,否則需要通知下游服務(wù)評(píng)估影響并同步進(jìn)行變更。
接下來(lái)可以更近一步,我們通常會(huì)通過(guò)引入消息中間件來(lái)對(duì)服務(wù)可用性依賴(lài)進(jìn)行解耦來(lái)達(dá)到Level1的級(jí)別。處在這一級(jí)別的服務(wù)之間,由于有明確的上下文邊界和依賴(lài)關(guān)系,消息的結(jié)構(gòu)也是上游系統(tǒng)來(lái)定義和維護(hù)的。那么如何基于業(yè)務(wù)場(chǎng)景來(lái)設(shè)計(jì)消息結(jié)構(gòu)、集成規(guī)則,以及支持兼容性的消息格式更新方式是這一級(jí)別需要關(guān)注的問(wèn)題。
四種耦合級(jí)別中,從高到低對(duì)團(tuán)隊(duì)的業(yè)務(wù)建模和技術(shù)能力要求越來(lái)越高,也隨著耦合度的減輕對(duì)新業(yè)務(wù)的適應(yīng)能力越來(lái)越強(qiáng)。
通過(guò)耦合級(jí)別來(lái)做出架構(gòu)上的權(quán)衡
那么基于上述耦合級(jí)別的區(qū)分,如何在設(shè)計(jì)架構(gòu)時(shí)進(jìn)行取舍呢?
對(duì)于處在level4級(jí)別的系統(tǒng),如果服務(wù)都在團(tuán)隊(duì)的職責(zé)范圍內(nèi),在保證高可用的前提下,在業(yè)務(wù)需求變化不頻繁的情況下,它暫時(shí)可以工作。如果系統(tǒng)由不同的團(tuán)隊(duì)維護(hù),或者需求變更頻繁的情況下,需要對(duì)業(yè)務(wù)模型進(jìn)行優(yōu)化,通過(guò)定義清楚的上下游關(guān)系以達(dá)到level2級(jí)別以增強(qiáng)架構(gòu)的適應(yīng)性。
對(duì)于處在level3級(jí)別的系統(tǒng),由于領(lǐng)域知識(shí)的耦合,服務(wù)都需要有其它領(lǐng)域的知識(shí)來(lái)完成自己的業(yè)務(wù)能力,隨著服務(wù)的增多很容易退化成網(wǎng)狀的依賴(lài),通常新的業(yè)務(wù)變更需要同時(shí)修改多個(gè)服務(wù),異步的集成方式也增加了擴(kuò)展和維護(hù)的難度。處在這一層級(jí)的系統(tǒng),優(yōu)先級(jí)還是通過(guò)優(yōu)化業(yè)務(wù)模型,定義清楚的上下游關(guān)系,至于是否需要使用異步的方式集成,需要綜合權(quán)衡業(yè)務(wù)的實(shí)時(shí)性和一致性要求來(lái)進(jìn)行權(quán)衡是過(guò)度到level2還是level1。
對(duì)于處在level2級(jí)別的系統(tǒng),由于系統(tǒng)的上下游關(guān)系相對(duì)清晰,重點(diǎn)可以放在采用合適的方式來(lái)完成上下游系統(tǒng)的集成上以實(shí)現(xiàn)。一般上游系統(tǒng)通過(guò)OHS/PL在保證發(fā)布語(yǔ)言不變化的情況下,可以獨(dú)立的進(jìn)行迭代更新;下游系統(tǒng)是通過(guò)跟隨或者添加防腐層來(lái)屏蔽上游業(yè)務(wù)模型變化帶來(lái)的影響,取決于業(yè)務(wù)模型變化的頻繁程度和添加新一層的成本。通常在綠地項(xiàng)目中,由于能從零開(kāi)始進(jìn)行業(yè)務(wù)建模和組建開(kāi)發(fā)團(tuán)隊(duì),在統(tǒng)一業(yè)務(wù)語(yǔ)言和明確上下游團(tuán)隊(duì)遵從關(guān)系的基礎(chǔ)上,采用新的服務(wù)構(gòu)建技術(shù)和實(shí)踐,在上下游服務(wù)間同時(shí)使用OHS/PL和ACL會(huì)比較好的隔離相互之間的影響。上游服務(wù)專(zhuān)注于領(lǐng)域能力的迭代并通過(guò)OHS/PL來(lái)發(fā)布功能,下游服務(wù)通過(guò)ACL來(lái)隔離上游變化對(duì)自身領(lǐng)域模型的影響,同時(shí)也可以按需來(lái)使用上游提供的新的功能。
對(duì)于處在level1級(jí)別的系統(tǒng),在業(yè)務(wù)和技術(shù)上都具備了松耦合的基礎(chǔ),但是此時(shí)需要警惕一種新的依賴(lài)產(chǎn)生。由于上游系統(tǒng)在消息格式的設(shè)計(jì)時(shí)沒(méi)有按照使用場(chǎng)景來(lái)設(shè)計(jì),或者消息格式不能很好的在向前兼容的情況下進(jìn)行更新,這帶來(lái)的后果是上游系統(tǒng)會(huì)成為下游新增業(yè)務(wù)的強(qiáng)依賴(lài),因?yàn)槿魏蔚男滦枨罂赡苄枰嫌蜗到y(tǒng)定義新的消息格式來(lái)支持,上游系統(tǒng)會(huì)成為響應(yīng)變化的瓶頸。如果服務(wù)在不同的團(tuán)隊(duì)中進(jìn)行維護(hù),那么帶來(lái)的后果就是團(tuán)隊(duì)間的沖突。在這個(gè)級(jí)別的依賴(lài)關(guān)系中,合理的消息模式以及兼容性設(shè)計(jì)是迭代演進(jìn)的關(guān)鍵。
消息集成通常分為兩種風(fēng)格Event Notification和Event-carried State Transfer,具體又可擴(kuò)展為以下幾種模式:
- 消息體包含領(lǐng)域事件發(fā)生后領(lǐng)域模型的最新?tīng)顟B(tài)和變更內(nèi)容
- 消息體包含領(lǐng)域事件發(fā)生后領(lǐng)域模型的最新?tīng)顟B(tài)
- 消息體包含領(lǐng)域事件發(fā)生后領(lǐng)域模型的變更內(nèi)容
- 消息體只包含領(lǐng)域事件發(fā)生后領(lǐng)域模型的標(biāo)識(shí),需要消費(fèi)者按需通過(guò)API來(lái)獲取相關(guān)信息
最后
以上是對(duì)于分布式系統(tǒng)中關(guān)于服務(wù)解耦的一些思考,希望上下游的思維能夠在做設(shè)計(jì)和系統(tǒng)開(kāi)發(fā)時(shí)給大家提供對(duì)照參考,幫助我們實(shí)現(xiàn)松耦合的目標(biāo),同時(shí)也有助于減小團(tuán)隊(duì)之間的依賴(lài)和摩擦。