今天主要跟你們分享一下什麼是 CQRS,以及在項目中如何去使用。前端
咱們日常最熟悉的就是三層架構,一般都是經過數據訪問層來修改或者查詢數據,通常修改和查詢使用的是相同的實體。而後經過業務層來處理業務邏輯,將處理結果封裝成DTO對象返回給控制層,再經過前端渲染。反之亦然。數據庫
這裏基本上是圍繞關係數據庫構建而成的「建立、讀取、更新、刪除」系統(即CRUD系統),此類系統在一些業務邏輯簡單的項目中可能沒有什麼問題,可是隨着系統邏輯變得複雜,用戶增多,這種設計就會出現一些性能問題。設計模式
咱們常常用到的解決方案就是對數據庫進行讀寫分離。讓主數據庫處理事務性的增、刪、改操做,讓從數據庫處理查詢操做,而後主從數據庫之間進行同步。可是這只是從DB角度處理了讀寫分離,從業務或者系統層面上來講,讀和寫的邏輯仍然是存放在一塊兒的,他們都是操做同一個實體對象。bash
這時候,CQRS 就該登場了。微信
簡單的說,CQRS(Command Query Responsibility Segration)就是一個系統,從架構上把 CRUD 系統拆分爲兩部分:命令(Command)處理和查詢(Query)處理。其中命令處理包括增、刪、改。架構
而後命令與查詢兩邊能夠用不一樣的架構實現,以實現CQ兩端(即Command Side,簡稱C端;Query Side,簡稱Q端)的分別優化。兩邊所涉及到的實體對象也能夠不一樣,從而繼續演變成下面這樣。app
固然了,CQRS 做爲一個讀寫分離思想的架構,在數據存儲方面,也沒有作過多的約束。因此 CQRS能夠有不一樣層次的實現。異步
CQRS 能夠有兩種實現方式。ide
1)CQ 兩端數據庫共享,只是在上層代碼上分離。這樣作的好處是可讓咱們的代碼讀寫分離,更容易維護,並且不存在 CQ 兩端的數據一致性問題,由於是共享一個數據庫的。這種架構是很是實用的(也就是我上面畫的那種)。性能
2)CQ 兩端不只代碼分離,數據庫也分離,而後Q數數據由C端同步過來。同步方式有兩種:同步或異步,若是須要 CQ 兩端的強一致性,則須要用同步;若是能接受 CQ 兩端數據的最終一致性,則可使用異步。C端能夠採用Event Sourcing(簡稱ES)模式,全部C端的最新數據所有用 Domain Event 表達便可;而要查詢顯示用的數據,則從Q端的 ReadDB(關係型數據庫)查詢便可。
說了這麼多,該怎麼實現呢?咱們以上面提到的第一種方式爲例:代碼層面實現分離,數據庫共享。這種方式在企業裏也很是實用。
首先有幾個概念須要介紹一下,CQRS 模式中,首先須要有 Command,這個 Command 命令會對應一個實體和一個命令的執行類。那整個系統中確定有不少不一樣的 Command,那麼還須要一個 CommandBus 來作命令的分發處理。
可能你們以爲比較抽象,我來寫幾行示例代碼,一看就明白了。假設有個訂單模塊,我要新增一個訂單信息。那麼根據上文的分析,須要有個新增命令以及對應的訂單實體(並不必定和數據庫的訂單實體徹底對應)。首先先建立一個命令接口(綁定命令對應的實體),接口內部有個該命令的處理方法。
public interface Command<T> {
Object execute(T commandModel);
}
複製代碼
OK,接下來咱們能夠建立訂單的新增命令了。
@Component
public class CreateOrderCommand implements Command<CreateOrderModel> {
@Override
public Object execute(CreateOrderModel model) {
// 具體的邏輯
}
}
複製代碼
到這裏,咱們寫好了具體的建立訂單命令的邏輯,那麼該命令須要放到 CommandBus 中去執行,因此咱們要寫這個 CommandBus。
@Component
public class CommandBus {
public <T> Object dispatch(Command<T> 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 模式的基本實踐,更多知識須要你們再深刻的去學習。
來源地:微信公衆號