程序員也許知道CRUD,CQRS是什麼你知道嗎?

CRUD系統

我們平常最熟悉的就是三層架構,通常都是通過數據訪問層來修改或者查詢數據,一般修改和查詢使用的是相同的實體。然後通過業務層來處理業務邏輯,將處理結果封裝成DTO對象返回給控制層,再通過前端渲染。反之亦然。

這裡基本上是圍繞關係數據庫構建而成的“創建、讀取、更新、刪除”系統(即CRUD系統),此類系統在一些業務邏輯簡單的項目中可能沒有什麼問題,但是隨著系統邏輯變得複雜,用戶增多,這種設計就會出現一些性能問題。

我們經常用到的解決方案就是對數據庫進行讀寫分離。讓主數據庫處理事務性的增、刪、改操作,讓從數據庫處理查詢操作,然後主從數據庫之間進行同步。但是這只是從DB角度處理了讀寫分離,從業務或者系統層面上來說,讀和寫的邏輯仍然是存放在一起的,他們都是操作同一個實體對象。


程序員也許知道CRUD,CQRS是什麼你知道嗎?


這時候,CQRS 就該登場了。


程序員也許知道CRUD,CQRS是什麼你知道嗎?


CQRS系統

簡單的說,CQRS(Command Query Responsibility Segration)就是一個系統,從架構上把 CRUD 系統拆分為兩部分:命令(Command)處理和查詢(Query)處理。其中命令處理包括增、刪、改。

然後命令與查詢兩邊可以用不同的架構實現,以實現CQ兩端(即Command Side,簡稱C端;Query Side,簡稱Q端)的分別優化。兩邊所涉及到的實體對象也可以不同,從而繼續演變成下面這樣。

當然了,CQRS 作為一個讀寫分離思想的架構,在數據存儲方面,也沒有做過多的約束。所以 CQRS可以有不同層次的實現。

CQRS 實現方式

CQRS 可以有兩種實現方式。

1)CQ 兩端數據庫共享,只是在上層代碼上分離。這樣做的好處是可以讓我們的代碼讀寫分離,更容易維護,而且不存在 CQ 兩端的數據一致性問題,因為是共享一個數據庫的。這種架構是非常實用的(也就是我上面畫的那種)。

2)CQ 兩端不僅代碼分離,數據庫也分離,然後Q數數據由C端同步過來。同步方式有兩種:同步或異步,如果需要 CQ 兩端的強一致性,則需要用同步;如果能接受 CQ 兩端數據的最終一致性,則可以使用異步。C端可以採用Event Sourcing(簡稱ES)模式,所有C端的最新數據全部用 Domain Event 表達即可;而要查詢顯示用的數據,則從Q端的 ReadDB(關係型數據庫)查詢即可。

CQRS 的簡單實現

說了這麼多,該怎麼實現呢?我們以上面提到的第一種方式為例:代碼層面實現分離,數據庫共享。這種方式在企業裡也非常實用。

首先有幾個概念需要介紹一下,CQRS 模式中,首先需要有 Command,這個 Command 命令會對應一個實體和一個命令的執行類。那整個系統中肯定有很多不同的 Command,那麼還需要一個 CommandBus 來做命令的分發處理。

可能大家覺得比較抽象,我來寫幾行示例代碼,一看就明白了。假設有個訂單模塊,我要新增一個訂單信息。那麼根據上文的分析,需要有個新增命令以及對應的訂單實體(並不一定和數據庫的訂單實體完全對應)。首先先創建一個命令接口(綁定命令對應的實體),接口內部有個該命令的處理方法。

blic interface Command {

Object execute(T commandModel);

}

OK,接下來我們可以創建訂單的新增命令了。

@Component

public class CreateOrderCommand implements Command<createordermodel> {/<createordermodel>

@Override

public Object execute(CreateOrderModel model) {

// 具體的邏輯

}

}

到這裡,我們寫好了具體的創建訂單命令的邏輯,那麼該命令需要放到 CommandBus 中去執行,所以我們要寫這個 CommandBus。

@Component

public class CommandBus {

public Object dispatch(Command cmd, T model) {

return cmd.excute(model);

}

}

可能大家會看著有點暈,甚至有點繞,沒關係,我解釋一下:這個 dispatch 方法就相當於分發執行,內部根據傳入的具體 Command 以及對應的 model,去執行該 Command 實現的邏輯。

好了,那我們在熟悉的 Controller 層該如何去調用呢?很簡單,如下:

@RestController

@RequestMapping(value = "/order")

public class OrderController {

@Resource

private GetOrderInfoService getOrderInfoService;

@Resource

private CreateOrderCommand createOrderCommand;

@Resource

private CommandBus commandBus;

@PostMapping(value = "/getInfo")

public Object getOrderInfo(GetOrderInfoModel model) {

return getOrderInfoService.getOrderInfos(model);

}

@PostMapping(value = "/creat")

public Object createOrderInfo(CreateOrderModel model) {

return commandBus.dispatch(createOrderCommand, model);

}

}

我還寫了一個獲取訂單信息的接口,大家有沒有發現,查詢和插入是不同的方式,插入走的是 CommandBus 分發到 CreateOrderCommand 去執行,而查詢則是直接走 service 層去查。這就是 CQRS 模式。

當然了,當命令越來越多的時候,也可以將 CommandBus 抽象出接口,可以根據業務需求,實現多個不同的 CommandBus 來分發命令。

除此之外,CQRS 還可以用在任務調度模塊中,不同的任務可以包含不同的 Command,實際中運用是非常廣泛的。

總結

CQRS 是一種思想很簡單清晰的設計模式,他通過在業務上分離操作和查詢來使得系統具有更好的可擴展性及性能,使得能夠對系統的不同部分進行擴展和優化。在 CQRS 中,所有的涉及到對 DB 的操作都是通過發送 Command,然後特定的 Command 觸發對應事件來完成操作,也可以做成異步的,主要看業務上的需求了。

CQRS 雖然在思想上簡單,但是實現上相對來說複雜些,也涉及到 DDD 的一些概念了,當然了,這篇文章主要是介紹以及演示 CQRS 模式的基本實踐,更多知識需要大家再深入的去學習。


分享到:


相關文章: