中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何使用CQRS避免查詢對模型設計的影響

發布時間:2021-09-06 15:58:54 來源:億速云 閱讀:143 作者:小新 欄目:web開發

這篇文章主要為大家展示了“如何使用CQRS避免查詢對模型設計的影響”,內容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領大家一起研究并學習一下“如何使用CQRS避免查詢對模型設計的影響”這篇文章吧。

模型設計時不要考慮查詢

按DDD設計領域模型時,有兩個很有用的原則:

  • 模型對展現技術要無感知,比如展現是用WEB,還是APP端,在做模型設計時不要考慮

  • 不要關心復雜查詢和報表的需求

那這樣領域模型設計出來后,經常受到的挑戰是查詢性能不好。相比于以數據為中心的設計思維,以領域對象為中心的設計思維設計出的數據存儲粒度更細,冗余更少,確實不利于查詢。

另外一個對查詢不友好的地方在于聚合這個概念。比如我想查一個訂單列表,列表中的每一行只需要訂單這個聚合根的數據,不需要把每一條訂單下的子實體都被查詢出來,那樣性能會很差。

可以使用JPA提供的懶加載來實現只加載聚合根,但是會帶來更多的其它的問題,懶加載已被認為是不被推薦使用的反模式。

大部分的業務系統都是讀多寫少,領域模型是重要,但能提供符合性能要求的查詢也很重要。DDD是怎么解決這個矛盾的呢?

模型對查詢不友好怎么辦

DDD推薦使用CQRS( Command Query Responsibility Segregation,  命令職責分離)這種模式來解決這個問題。

https://martinfowler.com/bliki/CQRS.html

我們來看看IDDD_Sample的 com.saasovation.collaboration.application.forum  下的應用服務,這些服務分成了兩類:

如何使用CQRS避免查詢對模型設計的影響

image.png

一類是 ApplicationService 一類是 QueryService 。

ApplicationService里的方法我們上篇文章里見過了,方法的入參是一個 Command 對象,返回值是一個 CommandResponse  對象。方法里通過 Repository 獲取聚合根,操作聚合根后,再通過 Repository  來持久化。這就是CQRS里的“C”,即命令(Command),它會改變系統的狀態。

在collaboration這個上下文的實現里,沒有把入參包裝成Command對象,可以參考agilepm上下文中ApplicationService的實現。IDDD_Sample為了演示各種風格,不同上下文的實現方式不太統一。也有人是把這種ApplicationService里的一個個方法變成一個個CommandHandler,可讀性更好,但本質上是一樣的。

CQRS里的“Q”指的是Query,顧名思義,查詢不會改變系統狀態的。

那分離是什么意思呢?

最粗淺的理解是把這些查詢方法放到 QueryService 里,而不是放到ApplicationService  里。但這只是個表象。我們看一個Query方法是怎么寫的:

public ForumDiscussionsData forumDiscussionsDataOfId(String aTenantId, String aForumId) {         return this.queryObject(                 ForumDiscussionsData.class,                 "select "                 +  "forum.closed, forum.creator_email_address, forum.creator_identity, "                 +  "forum.creator_name, forum.description, forum.exclusive_owner, forum.forum_id, "                 +  "forum.moderator_email_address, forum.moderator_identity, forum.moderator_name, "                 +  "forum.subject, forum.tenant_id, "                 +  "disc.author_email_address as o_discussions_author_email_address, "                 +  "disc.author_identity as o_discussions_author_identity, "                 +  "disc.author_name as o_discussions_author_name, "                 +  "disc.closed as o_discussions_closed, "                 +  "disc.discussion_id as o_discussions_discussion_id, "                 +  "disc.exclusive_owner as o_discussions_exclusive_owner, "                 +  "disc.forum_id as o_discussions_forum_id, "                 +  "disc.subject as o_discussions_subject, "                 +  "disc.tenant_id as o_discussions_tenant_id "                 + "from tbl_vw_forum as forum left outer join tbl_vw_discussion as disc "                 + " on forum.forum_id = disc.forum_id "                 + "where (forum.tenant_id = ? and forum.forum_id = ?)",                 new JoinOn("forum_id", "o_discussions_forum_id"),                 aTenantId,                 aForumId);     }

來自 ForumQueryService

我們發現這個查詢服務里即沒有使用領域對象也沒使用 Repository ,甚至 join  的兩個表是代表兩個聚合根的數據!在Query方法里可以直接查詢數據庫去返回一個查詢顯示用的DTO,這可以看做是分離的第一個意思,即Query里可以不使用領域對象和  Repository 。

我覺得可以不使用,意味著也可以使用

第二個意思是數據存儲的分離。最簡單的方式是處理 Command 的 ApplicationService/CommandHandler 訪問的是主庫,而  QueryService 訪問的是從庫。

在復雜一點,ApplicationService 可以訪問MySQL,而 QueryService  可以訪問Elasticsearch這種NoSQL,這時候需要做數據的同步。觸發數據同步,可以監聽領域事件來實現,也可以使用canal這種框架監聽binlog來實現。除了專門為查詢而設計的NoSQL本身有更好的查詢性能,同時,我們在數據結構設計上也可以專門為查詢來做優化,比如把多個關聯聚合的數據放到一起。

如何使用CQRS避免查詢對模型設計的影響

image.png

其實,IDDD_Sample的例子中的 collaboration  這個上下文就是這么實現的——領域對象的存儲使用的是LevelDB,查詢用的是MySQL。

這種方式我很多年前就看到一個團隊在用,他們也把Service分為WriteServcie和ReadService兩種,但是并沒有叫CQRS這個名字,也沒有總結為一種模式,而是憑著經驗這么做了。

事件溯源和CQRS

和CQRS經常一起出現的一種模式是事件溯源(Event  Sourcing)。事件溯源是一種更“前衛”的模式,它不存儲對象的狀態,相反,存儲影響其狀態的所有事件。

需要查詢對象的當前狀態時,只要把所有事件回放一遍就能得到。但是,這種回放太昂貴了,所以可以保留一份對象的最新快照,這就需要和CQRS模式結合。

如何使用CQRS避免查詢對模型設計的影響

image.png

IDDD_Sample例子中的 collaboration 這個上下文實際上使用的就是事件溯源。我們看它的 Repository  實現,存儲的是事件而不是實體本身:

事件溯源加上CQRS很“酷”,但一般不建議使用,實現的成本太高。

public class EventStoreForumRepository         extends EventStoreProvider         implements ForumRepository {      @Override     public void save(Forum aForum) {         EventStreamId eventId =                 new EventStreamId(                         aForum.tenant().id(),                         aForum.forumId().id(),                         aForum.mutatedVersion());          this.eventStore().appendWith(eventId, aForum.mutatingEvents());     } }

即使是只使用CQRS這一模式,也建議循序漸進,從簡單開始,最基本的,把QueryService 和 ApplicationService  分開,且在設計領域模型時不要受 QueryService 設計的影響。

以上是“如何使用CQRS避免查詢對模型設計的影響”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

怀安县| 蓬安县| 于田县| 临沧市| 六枝特区| 浏阳市| 盐津县| 中超| 隆子县| 蒙阴县| 邢台县| 红河县| 青田县| 城口县| 屏东县| 土默特右旗| 长宁县| 江华| 阜阳市| 永州市| 山东| 菏泽市| 雷山县| 沙坪坝区| 尚义县| 佛学| 阿拉尔市| 包头市| 大化| 兰州市| 孝义市| 东乡| 西昌市| 屯门区| 昌都县| 枣阳市| 砀山县| 延川县| 长春市| 垫江县| 大港区|