數(shù)字化轉(zhuǎn)型:服務(wù)化設(shè)計(jì)原則 | IDCF

來源:技術(shù)瑣話 作者:鐘華 本文由機(jī)械工業(yè)出版社獨(dú)家授權(quán)發(fā)布,中臺(tái)圣經(jīng)——《企業(yè)IT架構(gòu)轉(zhuǎn)型之道》作者鐘華新作《數(shù)字化轉(zhuǎn)型的道與術(shù):以平臺(tái)思維為核心支撐企業(yè)戰(zhàn)略可持續(xù)發(fā)展》
Fac?ade(外觀)模式
接下來在介紹服務(wù)化設(shè)計(jì)原則時(shí),會(huì)多次出現(xiàn)Fac?ade模式。
外觀模式的使用原理如圖4-11所示。

外觀模式的優(yōu)點(diǎn)如下:
松散耦合:外觀模式使得前臺(tái)應(yīng)用與中臺(tái)服務(wù)中心可以進(jìn)行松散耦合,讓服務(wù)中心內(nèi)部的模塊能更容易地?cái)U(kuò)展和維護(hù)。
簡(jiǎn)單易用:外觀模式讓服務(wù)中心的服務(wù)更加易用,前臺(tái)應(yīng)用不再需要了解服務(wù)中心內(nèi)部的實(shí)現(xiàn),也不需要跟服務(wù)中心內(nèi)部眾多的功能模塊進(jìn)行交互,只需跟外觀類交互就可以了。
更好地劃分訪問層次:通過合理使用外觀模式,可以更好地劃分訪問的層次,有些方法是對(duì)系統(tǒng)外的,有些方法是系統(tǒng)內(nèi)部使用的。把需要暴露給外部的功能集中到外觀中,這樣既方便客戶端使用,也很好地隱藏了內(nèi)部的細(xì)節(jié)。
DTO的使用
DTO可以將服務(wù)中心復(fù)雜或易變的數(shù)據(jù)對(duì)象對(duì)前臺(tái)應(yīng)用屏蔽,讓前臺(tái)具備更好的穩(wěn)定性。DTO是系統(tǒng)分層設(shè)計(jì)和服務(wù)化架構(gòu)中經(jīng)常使用的技術(shù),概念本身也容易理解,如圖4-12所示。

服務(wù)接口的設(shè)計(jì)原則

提供僅有幾個(gè)方法的很多服務(wù)。 數(shù)十或數(shù)百個(gè)操作均集中在幾個(gè)服務(wù)中。

ClaimEntryService {
createClaim(String userId);
ClaimItemDetails[] getClaimItems(int );
ClaimErrors[] validateClaim(int claimId);
void removeClaimItem(int claimId, int itemId);
int addClaimItem(int claimId, ClaimItemDetails details)
int submitClaim(int claimId);
}
ClaimApprovalService {
int approveClaimItem(int claimId, int itemId, String comment);
void approveClaim(claimId)
void returnClaim(claimId)
ClaimItemDetails[] getClaimItems(int );
ClaimErrors[] validateClaim(int claimId);
}
ClaimPaymentService {
void payClaim(int claimId);
}List <Article> getArticles(...)
List<ArticleSummary> getArticleSummaries(...)
Class Foo {
private Pattern regex;
}Class FooDto {
private String regex;
}6、隔離變化原則
當(dāng)服務(wù)中心核心領(lǐng)域模型的對(duì)象進(jìn)入前臺(tái)應(yīng)用中,要避免服務(wù)中心內(nèi)部的重構(gòu)或者模型變更導(dǎo)致前臺(tái)應(yīng)用也跟著變化。
比如前面描述的“文檔服務(wù)”,其中Article對(duì)象在服務(wù)中心內(nèi)部可能作為核心建模的領(lǐng)域模型,甚至作為對(duì)象和數(shù)據(jù)庫(kù)映射(O/R mapping)等。如果文檔服務(wù)給服務(wù)消費(fèi)者直接返回Article,即使沒有前面所說的冗余字段、復(fù)雜類型等問題,也可能讓服務(wù)外部用戶與服務(wù)內(nèi)部系統(tǒng)的核心領(lǐng)域模型產(chǎn)生一定的關(guān)聯(lián),甚至可能與O/R mapping機(jī)制、數(shù)據(jù)表結(jié)構(gòu)等產(chǎn)生關(guān)聯(lián),這樣一來,內(nèi)部的重構(gòu)很可能影響到服務(wù)外部的用戶。
同樣,可采用外觀模式和DTO作為中介者和緩沖帶,隔離內(nèi)外系統(tǒng),把內(nèi)部系統(tǒng)變化對(duì)外部的沖擊降到最低。
7、契約包裝
雖然使用了DTO和外觀模式將服務(wù)生產(chǎn)端的變化與服務(wù)消費(fèi)端進(jìn)行了隔離,但DTO和外觀模式可能被服務(wù)消費(fèi)端的程序到處引用,這樣消費(fèi)端程序就較強(qiáng)地耦合在服務(wù)契約上了。一旦契約更改,或者消費(fèi)端要選擇完全不同的服務(wù)提供方(有不同的契約),修改時(shí)工作量可能就非常大了。在較理想的面向服務(wù)設(shè)計(jì)中,可以考慮包裝遠(yuǎn)程服務(wù)訪問邏輯,也稱為服務(wù)代理(Delegate Service)模式,由消費(fèi)端自己主導(dǎo)定義接口和參數(shù)類型,并將服務(wù)調(diào)用轉(zhuǎn)發(fā)給真正的服務(wù)客戶端,從而讓服務(wù)使用者完全屏蔽服務(wù)契約。
服務(wù)代理示例如下:
//ArticlesService是消費(fèi)端自定義的接口
class ArticlesServiceDelegate implements ArticlesService {
//假設(shè)是某種自動(dòng)生成的service客戶端stub類
private ArticleFacadeStub stub;
public void deleteArticles(List<Long> ids) {
stub.deleteArticles(ids);
}
}在此示例的前臺(tái)應(yīng)用中,所有有關(guān)文檔服務(wù)調(diào)用的地方引用的都是ArticlesService,而不是“文檔服務(wù)”提供的ArticleFacadeStub,這樣就算服務(wù)提供端的ArticleFacadeStub發(fā)生了變更或者重構(gòu),也只需要在ArticlesService類中進(jìn)行相應(yīng)的調(diào)整,而無須更改更多的代碼。
8、服務(wù)無狀態(tài)原則
問:小明的賬號(hào)余額是多少? 答:320元。 問:他的信用額度是多少? 答:2000元。
問:小明的賬號(hào)余額是多少? 話務(wù)員1:320元。 此時(shí)通話中斷,被轉(zhuǎn)接到另一個(gè)話務(wù)員: 問:他的信用額度是多少? 話務(wù)員2:誰?
問:小明的信用額度是多少? 答:小明的信用額度是2000元。
ManageCustomerData {
insertCustomerRecord();
updateCustomerRecord();
//etc ... }接下來是使用名詞和動(dòng)詞短語及業(yè)務(wù)概念的服務(wù)定義:
CustomerService {
createNewCustomer();
changeCustomerAddress();
correctCustomerAddress();
// etc ... }比較明顯,第二個(gè)示例的易用性更好一些。在第二個(gè)示例中,服務(wù)的業(yè)務(wù)用途非常清楚,而不僅僅指示其輸出。因此,建議不要使用“update-CustomerRecord”(可以為出于任何原因進(jìn)行的任何更新),而使用“enable-OverdraftFacility(啟用透支能力)”。
與此類似,在客戶搬遷時(shí),我們使用“changeCustomerAddress”方法更改客戶地址;而在希望更正無效數(shù)據(jù)時(shí)使用“correctCustomerAddress”更正客戶地址,因?yàn)檫@樣很容易看出這兩個(gè)操作采用了不同的服務(wù)邏輯。
10、服務(wù)操作設(shè)計(jì)原則
這是對(duì)于服務(wù)操作命名設(shè)計(jì)原則的進(jìn)一步深化:應(yīng)當(dāng)使用具體的業(yè)務(wù)含義而不是泛型操作對(duì)操作進(jìn)行定義。例如,不要使用泛泛的update-CustomerDetails操作,而要?jiǎng)?chuàng)建changeCustomerAddress、recordCustomer-Marriage和addAlternativeCustomerContactNumber之類的操作。
此方法具有以下好處:
操作與具體業(yè)務(wù)場(chǎng)景對(duì)應(yīng)。此類場(chǎng)景可能不僅是簡(jiǎn)單地更新數(shù)據(jù)庫(kù)中的記錄。例如,更改地址或婚姻狀況可能需要更改其他業(yè)務(wù)模塊中的相關(guān)信息,比如婚姻狀況的修改可能會(huì)引起會(huì)員權(quán)益的改變。如果使用不太具體的操作(如UpdateCustomerDetails),則不適合實(shí)現(xiàn)此類業(yè)務(wù)場(chǎng)景。
各個(gè)操作接口將非常簡(jiǎn)單,且易于理解,從而提高易用性。
每個(gè)操作的更新單元有清楚的定義(在我們的示例中為地址、婚姻狀況和電話號(hào)碼)。在實(shí)現(xiàn)具有高并發(fā)性要求的系統(tǒng)時(shí),我們可以基于操作的要求采用更細(xì)粒度的鎖定策略,從而減少資源爭(zhēng)用。
針對(duì)操作中參數(shù)的設(shè)計(jì),應(yīng)采用粗粒度和靈活性強(qiáng)的參數(shù),目的是盡量減少因?yàn)樾枨笞兏鼛淼膮?shù)結(jié)構(gòu)變化。
以CreateNewCustomer操作的兩個(gè)接口為例。
采用細(xì)粒度參數(shù)的CreateNewCustomer操作接口如下:
int CreateNewCustomer(String familyName,String givenName,
String initials, int age,String address1,
String address2, String postcode // ... )采用單個(gè)粗粒度參數(shù)的CreateNewCustomer操作接口如下:
int CreateNewCustomer( CustomerDetails newDetails)上可依賴下。越上層的服務(wù)實(shí)現(xiàn)可以依賴下層的服務(wù),也可跨級(jí)依賴。 下不可依賴上。下層的服務(wù)實(shí)現(xiàn)和運(yùn)行一定不能依賴上層的服務(wù),否則就會(huì)出現(xiàn)因?yàn)樯蠈臃?wù)質(zhì)量問題和不穩(wěn)定的表現(xiàn)影響到下層的重要服務(wù),而下層服務(wù)的故障將會(huì)影響到依賴這一服務(wù)的所有平級(jí)服務(wù)中心和前臺(tái)應(yīng)用的情況,會(huì)出現(xiàn)嚴(yán)重的“雪崩”效應(yīng)。 平級(jí)可依賴,避免循環(huán)依賴。這一原則最典型的體現(xiàn)是業(yè)務(wù)中臺(tái)的各服務(wù)中心在服務(wù)層級(jí)中均屬于平級(jí),它們均有同級(jí)別的服務(wù)運(yùn)營(yíng)要求,是可以互相依賴的。 高級(jí)別不可依賴低級(jí)別。業(yè)務(wù)重要性明顯高的服務(wù)不能依賴業(yè)務(wù)重要性低的服務(wù),應(yīng)做好相應(yīng)的服務(wù)降級(jí),或者通過前臺(tái)業(yè)務(wù)隔離這種情況的服務(wù)依賴。
總 結(jié)



