CQRS模式應用Spring Boot+Scala框架集成開發


1. 什么是CQRS

  CQRS最早來自於Betrand Meyer(Eiffel語言之父,開-閉原則OCP提出者)在 Object-Oriented Software Construction 這本書中提到的一種 命令查詢分離 (Command Query Separation,CQS) 的概念。其基本思想在於,任何一個對象的方法可以分為兩大類:

  • 命令(Command):不返回任何結果(void),但會改變對象的狀態。
  • 查詢(Query):返回結果,但是不會改變對象的狀態,對系統沒有副作用。

根據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模式有一些優點:

  1. 分工明確,可以負責不同的部分
  2. 將業務上的命令和查詢的職責分離能夠提高系統的性能、可擴展性和安全性。並且在系統的演化中能夠保持高度的靈活性,能夠防止出現CRUD模式中,對查詢或者修改中的某一方進行改動,導致另一方出現問題的情況。
  3. 邏輯清晰,能夠看到系統中的那些行為或者操作導致了系統的狀態變化。
  4. 可以從數據驅動(Data-Driven) 轉到任務驅動(Task-Driven)以及事件驅動(Event-Driven).

在下場景中,可以考慮使用CQRS模式:

  1. 當在業務邏輯層有很多操作需要相同的實體或者對象進行操作的時候。CQRS使得我們可以對讀和寫定義不同的實體和方法,從而可以減少或者避免對某一方面的更改造成沖突
  2. 對於一些基於任務的用戶交互系統,通常這類系統會引導用戶通過一系列復雜的步驟和操作,通常會需要一些復雜的領域模型,並且整個團隊已經熟悉領域驅動設計技術。寫模型有很多和業務邏輯相關的命令操作的堆,輸入驗證,業務邏輯驗證來保證數據的一致性。讀模型沒有業務邏輯以及驗證堆,僅僅是返回DTO對象為視圖模型提供數據。讀模型最終和寫模型相一致。
  3. 適用於一些需要對查詢性能和寫入性能分開進行優化的系統,尤其是讀/寫比非常高的系統,橫向擴展是必須的。比如,在很多系統中讀操作的請求時遠大於寫操作。為適應這種場景,可以考慮將寫模型抽離出來單獨擴展,而將寫模型運行在一個或者少數幾個實例上。少量的寫模型實例能夠減少合並沖突發生的情況
  4. 適用於一些團隊中,一些有經驗的開發者可以關注復雜的領域模型,這些用到寫操作,而另一些經驗較少的開發者可以關注用戶界面上的讀模型。
  5. 對於系統在將來會隨着時間不段演化,有可能會包含不同版本的模型,或者業務規則經常變化的系統
  6. 需要和其他系統整合,特別是需要和事件溯源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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM