1. 什麼是CQRS
CQRS最早來自于Betrand Meyer(Eiffel語言之父,開-閉原則OCP提出者)在 Object-Oriented Software Construction 這本書中提到的一種 指令查詢分離 (Command Query Separation,CQS) 的概念。其基本思想在于,任何一個對象的方法可以分為兩大類:
|
---|
根據CQS的思想,任何一個方法都可以拆分為指令和查詢兩部分。比如:
private int i = 0;
private int Increase(int value) {
i += value;
return i;
}
這個方法,我們執行了一個指令即對變量i進行相加,同時又執行了一個Query,即查詢傳回了i的值,如果按照CQS的思想,該方法可以拆成Command和Query兩個方法,如下:
private void increaseCommand(int value) {
i += value;
}
private int queryValue() {
return i;
}
操作和查詢分離使得我們能夠更好的把握對象的細節,能夠更好的了解哪些操作會改變系統的狀态。當然CQS也有一些缺點,比如代碼需要處理多線程的情況。
CQRS是對CQS模式的進一步改進成的一種簡單模式。 它由Greg Young在CQRS, Task Based UIs, Event Sourcing agh! 這篇文章中提出。“CQRS隻是簡單的将之前隻需要建立一個對象拆分成了兩個對象,這種分離是基于方法是執行指令還是執行查詢這一原則來定的(這個和CQS的定義一緻)”。 |
---|
CQRS使用分離的接口将資料查詢操作(Queries)和資料修改操作(Commands)分離開來,這也意味着在查詢和更新過程中使用的資料模型也是不一樣的。這樣讀和寫邏輯就隔離開來了。
使用CQRS分離了讀寫職責之後,可以對資料進行讀寫分離操作來改進性能,可擴充性和安全。
2. 為什麼要引入CQRS
CQRS模式有一些優點:
- 分工明确,可以負責不同的部分
- 将業務上的指令和查詢的職責分離能夠提高系統的性能、可擴充性和安全性。并且在系統的演化中能夠保持高度的靈活性,能夠防止出現CRUD模式中,對查詢或者修改中的某一方進行改動,導緻另一方出現問題的情況。
- 邏輯清晰,能夠看到系統中的那些行為或者操作導緻了系統的狀态變化。
- 可以從資料驅動(Data-Driven) 轉到任務驅動(Task-Driven)以及事件驅動(Event-Driven).
在下場景中,可以考慮使用CQRS模式:
- 當在業務邏輯層有很多操作需要相同的實體或者對象進行操作的時候。CQRS使得我們可以對讀和寫定義不同的實體和方法,進而可以減少或者避免對某一方面的更改造成沖突
- 對于一些基于任務的使用者互動系統,通常這類系統會引導使用者通過一系列複雜的步驟和操作,通常會需要一些複雜的領域模型,并且整個團隊已經熟悉領域驅動設計技術。寫模型有很多和業務邏輯相關的指令操作的堆,輸入驗證,業務邏輯驗證來保證資料的一緻性。讀模型沒有業務邏輯以及驗證堆,僅僅是傳回DTO對象為視圖模型提供資料。讀模型最終和寫模型相一緻。
- 适用于一些需要對查詢性能和寫入性能分開進行優化的系統,尤其是讀/寫比非常高的系統,橫向擴充是必須的。比如,在很多系統中讀操作的請求時遠大于寫操作。為适應這種場景,可以考慮将寫模型抽離出來單獨擴充,而将寫模型運作在一個或者少數幾個執行個體上。少量的寫模型執行個體能夠減少合并沖突發生的情況
- 适用于一些團隊中,一些有經驗的開發者可以關注複雜的領域模型,這些用到寫操作,而另一些經驗較少的開發者可以關注使用者界面上的讀模型。
- 對于系統在将來會随着時間不段演化,有可能會包含不同版本的模型,或者業務規則經常變化的系統
- 需要和其他系統整合,特别是需要和事件溯源Event Sourcing進行整合的系統,這樣子系統的臨時異常不會影響整個系統的其他部分。
3. 系統如何實作
CQRS采用Spring MVC+Scala+Scala sql是實作。
1) Spring MVC及封裝的Spring Boot作為一個Javaer這裡就不再贅述。
2) 為什麼要用Scala語言開發:
首先,Scala也是基于JVM的語言,可以直接調用Java API及強大三方庫; 其次,Scala = OOP + FP;Java 8以前隻有OOP。Scala 是面向對象及面向函數程式設計都支援都很好的語言; 最後,Scala學好後,程式設計效率要比Java高很多; 另外,Scala很多特性Java一直在模仿但是重來沒超越。比如,Lambda,流式處理,模式比對,隐私轉換,天然的線程安全,可以作為腳本語言REPL,macro支援,強大的類型系統,大名鼎鼎的Akka架構等...。Spark,kafka等優秀開源項目都是Scala寫的。 注意,業務開發簡單Scala學習就夠了。如果深入學習Scala學習成本要比Java高 |
---|
3) 為什麼用Scala sql代替MyBatis:
Scala-sql (原作者請參考)是一個輕量級的 JDBC 庫,提供了在scala中通路關系型資料庫的一個簡單的API,其定位是對面 scala開發者,提供一個可以替換 spring-jdbc, MyBatis 的資料通路庫。相比 spring-jdbc, MyBatis, Hibernate 等庫,scala-sql有一些自己獨特的特點: 1. SQL語句與對象直接映射。函數式支援。scala-sql支援從ResultSet到Object的映射,在1.0版本中,是映射到JavaBean,而在2.0中,則是映射到Case Class。選擇 Case Class的原因也是為了更好的支援函數式程式設計。(支援Case Class與函數式程式設計有什麼關系?函數式程式設計的精髓就是無副作用的值變換, immutable才是函數式程式設計的真愛) 2. 編譯期間的SQL文法檢查。 這個特性可以讓開發變得更加便捷一些,如果有SQL文法錯誤,或者存在錯誤拼寫的字段名、表名等情況,在編譯時期就能 發現錯誤,可以更快的暴露錯誤,縮短測試周期。 3. 零學習成本。隻要你會寫Sql腳本,了解了Scala基本文法。你就可以直接使用scala-sql對Mysql做增删改查。 4. 面向scala語言。是以,如果選擇 java 或者其他程式設計語言,scala-sql基本上沒有意義。而spring-jdbc, iBatis等顯然不受這個限制。 5. 概念簡單。 scala-sql 為 java.sql.Connection, javax.sql.DataSource 等對象擴充了:executeUpdate、rows、foreach等方法, 但其語義完全與 jdbc 中的概念是一緻的。 熟悉jdbc的程式員,并且熟悉scala文法的程式員,scala-sql基本上沒有新的概念需要學習。 6. 強類型。 scala-sql目前是2.0版本,使用了sql"sql-statement" 的插值文法來替代了Jdbc中複雜的 setParameter 操作,并且是強類型和 可擴充的。 強類型:如果你試圖傳入 URL、FILE 等對象時,scala編譯器會檢測錯誤,提示無效的參數類型。 可擴充:不同于JDBC,隻能使用固定的一些類型,scala-sql通過Bound Context:JdbcValueAccessor來擴充,也就是說,如果你定義了 對應的擴充,URL、FILE也是可以作為傳給JDBC的參數的。(當然,需要在JdbcValueAccessor中正确的進行處理)。 |
---|
4) 代碼結構
Demo源碼請參考:
sring-scala-demo
每天一點成長,歡迎指正!