本文討論的是如何使用CQRS實現(xiàn)API設(shè)計,。概述下面是名為Command / Query Responsibility Segregation(CQRS)的設(shè)計模式: 返回數(shù)據(jù) 做出改變查詢 ?? ?命令 ? ?? 查詢和命令是兩種分離的API,。為何使用這種模式,?我喜歡它有幾個原因。作為API的消費者,,我永遠(yuǎn)不必?fù)?dān)心使用API出現(xiàn)異常了,;相反,我能確切地知道哪些API調(diào)用會專門應(yīng)付對系統(tǒng)的更改請求,,沒有含糊之處,。這使得API 易于推理。我曾經(jīng)嘗試創(chuàng)建一個統(tǒng)一的界面來完成兩者,,但是隨著時間推移,,出現(xiàn)“服務(wù)于兩個主人“”的典型問題,單一界面變得更加混亂,。時間長了會發(fā)生:“我們不使用這個字段,,為什么我們要更新它?,?!?回應(yīng):“我不知道,但繼續(xù)這樣做或會出問題,?!?因此,CQRS的核心是一個關(guān)注點分離的特定應(yīng)用,,即良好的組織實踐?,F(xiàn)在我們已經(jīng)對模式進(jìn)行了介紹,我將介紹一些實現(xiàn)細(xì)節(jié)和經(jīng)驗教訓(xùn),。消息我認(rèn)為每個查詢或命令都是一條消息,。這意味著任何客戶端系統(tǒng)都可以將這些表達(dá)為沒有方法的普通數(shù)據(jù)(類或結(jié)構(gòu)),。然后以線形格式(如JSON或CapnProto或w / e)輕松傳輸它們。每條消息都有一個名稱 - 通常只是類/結(jié)構(gòu)名稱 - 它在API中唯一標(biāo)識它,。如SearchCustomers(查詢)或DeactivateCourse(命令),。名稱用于標(biāo)識請求的操作,然后將其與消息解析器和處理函數(shù)進(jìn)行匹配,。安全授權(quán)由此很簡單,,只要保留一個用戶名單,允許這些用戶可發(fā)送哪些消息名稱,;然后在處理任何用戶的消息之前檢查該列表,。運營命令和查詢應(yīng)該如何工作似乎很明顯。但是我發(fā)現(xiàn)了一些細(xì)微差別,。查詢:查詢是很簡單的:1. API偵聽 /query/[Query Name]2. 驗證用戶是否具有[Query Name]權(quán)限3. 反序列化查詢消息4. 將查詢消息傳遞給其處理函數(shù),,該函數(shù)將:5. 驗證查詢消息6. 從數(shù)據(jù)庫加載和轉(zhuǎn)換數(shù)據(jù)7. 序列化并返回數(shù)據(jù)命令:命令的目的是在系統(tǒng)上執(zhí)行一些業(yè)務(wù)操作。在實踐中,,我們注意到命令是否需要更改一個或多個實體,。出于架構(gòu)原因,如何處理多個實體更改非常重要,。在這種情況下,,實體意味著某個邏輯單元。在高度規(guī)范化的表中,,實體可能包含父對象和一對多關(guān)系的任何子對象,。在DDD術(shù)語中,你可以將其稱為聚合,。在事件溯源中,,這是一個事件流。可擴(kuò)展性你可以在單個事務(wù)中執(zhí)行多實體更改,,以實現(xiàn)全有或全無的事務(wù)語義,。這種方法很適合,但它限制了可伸縮性,。要參與事務(wù),,所有受影響的實體必須位于同一數(shù)據(jù)庫節(jié)點上。如果它們位于不同的節(jié)點上,,則發(fā)生分布式事務(wù)(如果數(shù)據(jù)庫支持),。隨著負(fù)載的增加,,分布式事務(wù)將逐漸變慢,。跨實體事務(wù)是企業(yè)內(nèi)部業(yè)務(wù)應(yīng)用程序的有效方法,,但對于公開的互聯(lián)網(wǎng)服務(wù),,也許不是,。像互聯(lián)網(wǎng)這種更加大規(guī)模級別應(yīng)用,我們的方法是僅使用單實體命令進(jìn)行更改,。當(dāng)用例需要更改多個實體時,,請使用元命令(自身不做任何改變),而不是編排并運行單實體命令,。我將單實體命令稱為“基本命令”,,將多實體命令稱為“工作流程”。當(dāng)工作流調(diào)用基本命令時,,這些命令可能會失敗,,工作流程會以業(yè)務(wù)用例的方式處理故障。這可能意味著需要忽略失敗并作為業(yè)務(wù)錯誤/警告返回給客戶,,或采取補(bǔ)償回退措施,。客戶端工作流你可以在客戶端實現(xiàn)工作流 - 讓UI編排所有必要的基本命令。但是,,我不在客戶端實現(xiàn)工作流,,而在API端實現(xiàn)工作流,主要理由是清晰(特別是安全性),。我用一個真實例子來說明,。我們有培訓(xùn)師的角色,這個角色不能創(chuàng)建課程,,但是,,他們可以記錄他們提供給員工的培訓(xùn)。記錄培訓(xùn)時可能需要創(chuàng)建一個有限選項的新課程,。將記錄培訓(xùn)作為API工作流執(zhí)行,,可以將其表示為單個細(xì)化權(quán)限:“培訓(xùn)師可以記錄培訓(xùn),但不能創(chuàng)建課程,?!?在權(quán)限UI上選中一個選項,而不選中另一個選項,。如果采取客戶端工作流,,執(zhí)行上述相同的操作,我們看看會怎樣,?我們需要添加一個基本命令:創(chuàng)建培訓(xùn)師課程,,然后管理員用戶必須被告知:“要給某人錄制培訓(xùn)的權(quán)限,你必須檢查Create Trainer Course并且Permission X和Permission Y,?!?那么這就給最終用戶造成的負(fù)擔(dān),需要他做這些流程。在這里,,我們還可以創(chuàng)建一個偽命令,,僅用于權(quán)限目的,映射到所需的基本命令,。這就將負(fù)擔(dān)又轉(zhuǎn)移給開發(fā)人員,。我不喜歡這些結(jié)果中的任何一個,所以我更喜歡API端工作流,。指導(dǎo)原則在實施CQRS API時,,人們會提出一些非常常見的問題。1. 返回錯誤與返回數(shù)據(jù)不同,。一個流行的誤解是命令應(yīng)該什么都不返回,。實際是命令應(yīng)該返回一些東西。它們返回有關(guān)操作本身的元信息(無論是成功還是失敗以及為什么,。這與返回業(yè)務(wù)數(shù)據(jù)非常不同,,后者是查詢的工作。2. 命令可以在不進(jìn)行更改的情況下成功,。命令可以進(jìn)行0次或更多次更改,。換句話說,“進(jìn)行更改”是命令的目的,,而不是所需的結(jié)果,。因此,對于成功運行的命令完全有效,,但不會導(dǎo)致任何更改,。在運行命令之前和之后比較實體,如果它們完全相同,,那么就是進(jìn)行0更改并成功返回,。3. 命令處理代碼可以調(diào)用查詢。這似乎違反了CQRS原則,。命令的內(nèi)部除了“進(jìn)行更改”之外,,它沒有任何意思。因此,,可以在命令中運行查詢,,才能獲取命令所需的一些信息,但是要小心一點,。4. 自動遞增ID不應(yīng)是主鍵ID,。常常需要返回自動生成的ID,因為自動增量ID非常方便,,但是也有問題:重復(fù),。場景:用戶填寫表單用來創(chuàng)建新實體并點擊提交,。請求超時。自動增量冒險:如果自動增量字段是您唯一的ID,,則你的應(yīng)用無法知道請求是否成功,。對這種情況的補(bǔ)救措施通常取決于用戶的意識和參與,。如果用戶再次點擊提交(非??赡埽窍惹暗恼埱笕绻_實創(chuàng)建了實體,,盡管超時,,那么現(xiàn)在有兩個具有不同ID的相同實體。要正確清理,,用戶現(xiàn)在應(yīng)該搜索重復(fù)項并刪除冗余實體??(極不可能),。或者,在超時之后,,用戶可以搜索他們可能創(chuàng)建的實體,。如果他們找不到,請再回來填寫表單,。根據(jù)我的經(jīng)驗,,這種情況不太可能。如果培訓(xùn)用戶習(xí)慣于這樣思考,,則可能會發(fā)生這種情況,。還可以添加重復(fù)檢查的外部系統(tǒng),例如保持對已查看操作及其結(jié)果的記憶,。但有更好的方法......預(yù)先生成的ID(冒險):在用戶甚至開始鍵入任何內(nèi)容之前,,在加載表單時就生成(或從服務(wù)器請求)ID。在用戶被告知請求超時后,,她再次點擊提交,。界面就會使用相同的預(yù)生成ID發(fā)送與之前相同的完全相同的請求。如果數(shù)據(jù)庫沒有重復(fù)會成功,,否則API響應(yīng):“此實體已存在,。” 如果UI可以識別出這個特定錯誤,,它可以假裝它正常成功,。這種冒險帶來了更好的用戶體驗,沒有重復(fù)的機(jī)會,。我們的策略:我們傾向于使用UUID進(jìn)行所有標(biāo)識,。它們很容易在許多平臺上生成,。他們無視趨勢分析,。我們的大多數(shù)創(chuàng)建表單都必須運行查詢(例如,,獲取下拉列表數(shù)據(jù)),因此我們會在查詢結(jié)果中包含一個新的UUID,。結(jié)論命令是變動的守門人,。查詢則是知識庫,這是CQRS,。我發(fā)現(xiàn)這種模式使我走向正確的方向,。它也是一種多功能的模式。它不關(guān)心你的部署的是單體還是微服務(wù),。甚至可以將命令和查詢拆分為各自獨立的服務(wù),,實現(xiàn)讀寫負(fù)載分離。但請記住,,這只是一個大系統(tǒng)中的一個部分,,而不是適合每個系統(tǒng)的通用工具。CQRS模式在后端系統(tǒng)的邊界內(nèi)能很好地工作,,與客戶端應(yīng)用程序連接,。與任何模式一樣,只有在適當(dāng)?shù)那闆r下應(yīng)用它時才有用,。 |
|
來自: 漢無為 > 《單體架構(gòu)》