掃二維碼與項目經(jīng)理溝通
我們在微信上24小時期待你的聲音
解答本文疑問/技術(shù)咨詢/運營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流
當我們系統(tǒng)中的數(shù)據(jù)模型層級較少時,數(shù)據(jù)模型足夠簡單時,模型與數(shù)據(jù)庫可以直接進行映射。這種簡單數(shù)據(jù)模型使我們不需要針對其相互關(guān)系進行復雜的建模設(shè)計,直接在工程中使用經(jīng)典的三層模型就足以支撐項目需求。

成都創(chuàng)新互聯(lián)公司是專業(yè)的涇源網(wǎng)站建設(shè)公司,涇源接單;提供網(wǎng)站建設(shè)、網(wǎng)站制作,網(wǎng)頁設(shè)計,網(wǎng)站設(shè)計,建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進行涇源網(wǎng)站開發(fā)網(wǎng)頁制作和功能擴展;專業(yè)做搜索引擎喜愛的網(wǎng)站,專業(yè)的做網(wǎng)站團隊,希望更多企業(yè)前來合作!
對于這種簡單系統(tǒng),過度設(shè)計會增加后續(xù)維護、重構(gòu)的成本(并不能保證預設(shè)計能完美符合后續(xù)需求)。同時,對于簡單系統(tǒng),我們大部分的需求都只涉及其中的少量數(shù)據(jù)模型邏輯處理。
而我們直接對數(shù)據(jù)模型進行CURD就能滿足需求,進而的結(jié)論就是:
如果我們的系統(tǒng)具有一定復雜性,這種復雜性可能是源于訪問頻次、數(shù)據(jù)量或者是數(shù)據(jù)模型數(shù)量。這時候我們遇到的問題是數(shù)據(jù)在查詢和更新的需求差距逐漸變大。
根據(jù)以上舉例我們可以發(fā)現(xiàn),當我們的需求具有一定的復雜性后,根據(jù)引入復雜性的不同,會導致系統(tǒng)功能上需要用更加復雜的設(shè)計來對需求的復雜性進行支撐。同時我們也可以發(fā)現(xiàn),引入的不同復雜性在增刪改和查詢方面的帶來的功能需求差別很大。
所以:
如果我們對系統(tǒng)整體的構(gòu)建與設(shè)計有了更高的可維護性與可擴展性要求,以至于我們需要使用DDD來設(shè)計整個系統(tǒng)。
在這種情況下往往模型中具有相對復雜的模型關(guān)系,在增刪改時我們需要將所有請求封裝為領(lǐng)域?qū)ο螅员愠绦蚩梢曰陬I(lǐng)域模型完成大量復雜的校驗、業(yè)務(wù)邏輯。而在查詢需求時,我們常常需要組織跨領(lǐng)域數(shù)據(jù)來完成一個列表中數(shù)據(jù)內(nèi)容的展示。所以:
根據(jù)第一節(jié)中的內(nèi)容我們可以發(fā)現(xiàn),在進行系統(tǒng)架構(gòu)設(shè)計時,當系統(tǒng)出現(xiàn)復雜性后存在一個核心問題:
這種差異帶來的直接結(jié)果就是在系統(tǒng)開發(fā)的過程中,針對增刪改和查詢操作的業(yè)務(wù)設(shè)計上差異會比較大。如果舉幾個例子來說的話,比如:
由于存在增刪改與查詢邏輯有差異的這個問題,為了更好的針對差異進行抽象,我們可以將它們分開進行設(shè)計。也就是我們的CQRS模式,即命令查詢的責任分離Command Query Responsibility Segregation模式。其中我們稱增刪改為命令型操作。
CQRS本質(zhì)上是一種讀寫分離設(shè)計思想,這種框架設(shè)計模式將命令型業(yè)務(wù)和查詢型業(yè)務(wù)分開單獨處理。通過這種方式,CQRS可以針對命令和查詢單獨進行業(yè)務(wù)模型上的設(shè)計,從而用更加適合各自場景的方案與組件來提供能力。
查詢操作并不會修改數(shù)據(jù)庫中的內(nèi)容,所以查詢本身是一種冪等操作,以同一個查詢條件在系統(tǒng)不改變的情況下反復執(zhí)行會返回相同的結(jié)果,我們可以針對這種特性提供數(shù)據(jù)緩存來提高系統(tǒng)性能;同時因為不影響數(shù)據(jù)庫,查詢邏輯是不會產(chǎn)生數(shù)據(jù)一致性問題。查詢往往會存在較高的使用頻率。
命令操作會直接修改數(shù)據(jù)庫,并針對多個領(lǐng)域模型的情況下我們需要增加來保證操作的原子性。而對于一個命令操作,我們往往是不直接依賴命令的返回值的,所以通??梢援惒綀?zhí)行命令操作。對于一般系統(tǒng)來說,往往命令操作的使用頻次會較低。
由于CQRS的本質(zhì)是對于讀寫操作的分離,所以比較簡單的CQRS的做法是:
這種做法在不對數(shù)據(jù)庫進行分離設(shè)計的情況下,CQ兩端在上層代碼進行分離個字單獨維護,例如命令型的都用xxxManagerController、xxxManagerService來定義,而查詢則直接用xxxController、xxxService定義。
因為使用同一個數(shù)據(jù)庫,所以沒有CQ兩端的數(shù)據(jù)一致性問題。但因為已經(jīng)對上層代碼進行了抽離,所以可以滿足一些設(shè)計特性如:
這種方案可以滿足代碼邏輯上的分離維護,但由于是使用同一數(shù)據(jù)庫表,所以無法根據(jù)CQ兩種業(yè)務(wù)的特點單獨進行模型設(shè)計。
在代碼分離的基礎(chǔ)上,我們可以再將數(shù)據(jù)存儲的模型進行物理分離,讀取存儲可以是寫入存儲的只讀副本,使用多個只讀副本可以提高查詢性能;也可能為讀取模型單獨設(shè)計庫表。單獨對查詢和更新進行模型設(shè)計可以減小設(shè)計和實現(xiàn)的難度。并且此時讀取數(shù)據(jù)庫可使用自己的已針對查詢進行優(yōu)化的數(shù)據(jù)架構(gòu)。比如讀數(shù)據(jù)庫可以直接存儲查詢數(shù)據(jù)寬表從而避免進行join操作或者復雜的查詢映射。甚至可以針對讀取操作使用mongo或者es等nosql數(shù)據(jù)庫對查詢邏輯進行增強。
分離后的數(shù)據(jù)將存在在不同的數(shù)據(jù)庫中,Q的數(shù)據(jù)由C端同步過來。通常,這是通過在每次更新數(shù)據(jù)庫時使寫入模型發(fā)布事件來實現(xiàn)的。 而說到數(shù)據(jù)同步則就有同步執(zhí)行和異步執(zhí)行兩種方案:
同樣的,這種同步也可以解釋為對緩存進行的更新,即:查詢數(shù)據(jù)庫是使用緩存,而寫入數(shù)據(jù)庫使用普通MySQL,兩者之間數(shù)據(jù)同步通過領(lǐng)域事件實現(xiàn)最終一致性。
進一步的,由于命令操作實際上是對“操作”進行的記錄,而只有查詢才需要將所有的操作進行匯總展示。基于這種思想,可以使用事件溯源EventSourcing模式來進行命令操作的記錄。在這種方案下,保存記錄時更新的不是當前的記錄,而是會導致狀態(tài)變化的事件日志,每個事件表示對數(shù)據(jù)所作的一系列更改,而我們可以通過重播事件構(gòu)造數(shù)據(jù)當前的狀態(tài)(可以參考Mysql的Binlog設(shè)計)。這種記錄的優(yōu)點是可以根據(jù)回放,重現(xiàn)每一次狀態(tài)變更的時間點以及變更軌跡。而查詢則可以根據(jù)當前狀態(tài)的快照來為查詢提速。來自于網(wǎng)絡(luò)的架構(gòu)圖:
這種設(shè)計模式聽起來就比較復雜,但是卻有很多好處,例如:實現(xiàn)透明的分布式處理,當使用事件作為狀態(tài)改變的引擎時,你可以通過實現(xiàn)多任務(wù)并發(fā)處理,比如通過JVM并行計算或事件消息總線機制,事件能夠很容易序列化,并在多個服務(wù)器之間傳送。同時因為是保留的操作記錄,可以在回放的時候?qū)τ诋惓2僮鲾?shù)據(jù)進行過濾,從而增加了數(shù)據(jù)的魯棒性。
如果希望使用CQRS,根據(jù)你希望實現(xiàn)的系統(tǒng)性能,你需要評估當前系統(tǒng)架構(gòu)以及個人經(jīng)驗是否有以下能力:
對于以下場景不建議引入CQRS:
如果系統(tǒng)存在一定的復雜性,并且有以下的特點,則可以根據(jù)特點,選擇適合的CQRS實現(xiàn)方式。
總的來說,CQRS是處理復雜問題的一種具體實現(xiàn)方案,常用于配合DDD使用。
總結(jié)CQRS 的主要優(yōu)點包括:

我們在微信上24小時期待你的聲音
解答本文疑問/技術(shù)咨詢/運營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流