掃二維碼與項(xiàng)目經(jīng)理溝通
我們在微信上24小時期待你的聲音
解答本文疑問/技術(shù)咨詢/運(yùn)營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流
微服務(wù)的興起以及現(xiàn)代軟件架構(gòu)對可擴(kuò)展性、靈活性和可維護(hù)性的需求導(dǎo)致開發(fā)人員接受各種設(shè)計(jì)模式。

站在用戶的角度思考問題,與客戶深入溝通,找到白云網(wǎng)站設(shè)計(jì)與白云網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗(yàn),讓設(shè)計(jì)與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個性化、用戶體驗(yàn)好的作品,建站類型包括:網(wǎng)站制作、做網(wǎng)站、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣、域名注冊、虛擬空間、企業(yè)郵箱。業(yè)務(wù)覆蓋白云地區(qū)。
近年來備受關(guān)注的一種模式是命令查詢職責(zé)分離 (CQRS) 模式。CQRS 特別適合命令(改變狀態(tài))和查詢(讀取狀態(tài))之間有明顯區(qū)別的系統(tǒng)。在本文中,我們將深入研究 CQRS 并了解如何使用 Spring 微服務(wù)來實(shí)現(xiàn)它。
命令查詢職責(zé)分離(CQRS)是一種架構(gòu)模式,建議將數(shù)據(jù)修改操作(命令)與數(shù)據(jù)檢索操作(查詢)分離。這種分離允許開發(fā)專門的模型來查詢和更新數(shù)據(jù),從而增強(qiáng)應(yīng)用程序的清晰度和可擴(kuò)展性。
CQRS 的核心目標(biāo)是通過確保每個任務(wù)負(fù)責(zé)單個操作(命令或查詢,但絕不會同時負(fù)責(zé)兩者)來簡化任務(wù)。
CQRS 并不是一個全新的概念。它的根源可以追溯到 CQS(命令查詢分離),這是 Eiffel 編程語言的創(chuàng)建者 Bertrand Meyer 推廣的一項(xiàng)原則。雖然 CQS 主要是關(guān)于方法——聲明方法應(yīng)該執(zhí)行命令或回答查詢,但 CQRS 將這一原則擴(kuò)展到應(yīng)用程序的架構(gòu)級別,建議不同的架構(gòu)組件處理命令和查詢。
微服務(wù)架構(gòu)的興起放大了 CQRS 的必要性。在分布式系統(tǒng)中,服務(wù)通常需要自治和高度解耦,CQRS 提供了一條清晰的路徑。每個微服務(wù)都可以采用 CQRS 模式,確保其處理命令和查詢的內(nèi)部機(jī)制從其他服務(wù)中抽象出來。這也與領(lǐng)域驅(qū)動設(shè)計(jì)(DDD)非常吻合,其中領(lǐng)域事件可以觸發(fā)不同微服務(wù)中的命令。
雖然 CQRS 提供了許多好處,但它也面臨著挑戰(zhàn):
Spring 生態(tài)系統(tǒng)擁有豐富的工具和框架,非常適合在微服務(wù)環(huán)境中實(shí)現(xiàn) CQRS 模式。
第一步是建立一個基本的 Spring Boot 項(xiàng)目。如果您是 Spring Boot 新手,您可以使用Spring Initializr輕松初始化您的項(xiàng)目?;疽蕾図?xiàng)包括 Spring Web、Spring Data JPA 以及您喜歡的任何數(shù)據(jù)庫連接器。
在基于 Spring 的 CQRS 系統(tǒng)中,命令表示更改某些狀態(tài)的意圖,命令處理程序處理這些命令。
示例命令:
public class CreateUserCommand {
private final String userId;
private final String username;
// Constructor, getters, and other methods...
}對于每個命令,都定義了相應(yīng)的命令處理程序。該處理程序包含處理命令的實(shí)際邏輯:
@Service
public class CreateUserCommandHandler implements CommandHandler {
@Autowired
private UserRepository userRepository;
@Override
public void handle(CreateUserCommand command) {
User user = new User(command.getUserId(), command.getUsername());
userRepository.save(user);
}
} 在領(lǐng)域驅(qū)動設(shè)計(jì)(DDD)的背景下,狀態(tài)突變通常發(fā)生在聚合上。這些聚合可確保在保留任何更改之前遵守所有域規(guī)則。
類似地,查詢表示讀取某些狀態(tài)的請求,查詢處理程序處理這些請求。
查詢示例:
public class GetUserByIdQuery {
private final String userId;
// Constructor, getters, and other methods...
}對應(yīng)的查詢處理程序:
@Service
public class GetUserByIdQueryHandler implements QueryHandler {
@Autowired
private UserRepository userRepository;
@Override
public User handle(GetUserByIdQuery query) {
return userRepository.findById(query.getUserId()).orElse(null);
}
} 雖然 CQRS 提供了隔離機(jī)制,但可以使用事件源來簡化命令和查詢之間狀態(tài)的維護(hù)。Axon 框架是一種有助于使用 Spring 實(shí)現(xiàn) CQRS 和事件溯源的流行框架。
對于 Axon,事件在命令處理后發(fā)布。這些事件可以被持久化,然后用于重新創(chuàng)建聚合的狀態(tài)。它還有助于保持查詢端與命令端同步。
鑒于微服務(wù)的分布式特性,在服務(wù)之間實(shí)現(xiàn)異步通信通常是有益的。Apache Kafka 可以集成到 Spring 生態(tài)系統(tǒng)中,以實(shí)現(xiàn)強(qiáng)大的事件驅(qū)動架構(gòu),這在 CQRS 設(shè)置中特別有用。
命令端產(chǎn)生的事件可以推送到Kafka主題中,查詢端可以使用這些事件來更新自己的數(shù)據(jù)存儲。這確保了命令端和查詢端之間的解耦,使系統(tǒng)更具彈性和可擴(kuò)展性。
雖然 CQRS 專注于分離命令和查詢職責(zé),但事件溯源可確保應(yīng)用程序狀態(tài)的每次更改都被捕獲在事件對象中,并按照它們應(yīng)用于同一聚合的順序存儲。這種方法允許您重建過去的狀態(tài),并且與 CQRS 結(jié)合使用時特別有利。
事件溯源是關(guān)于持久化域事件而不是狀態(tài)本身。這些事件捕獲狀態(tài)轉(zhuǎn)換。通過重放它們,可以重建聚合的當(dāng)前狀態(tài)。
例如,您將存儲所有交易(例如存款和取款等事件),而不是存儲銀行帳戶的當(dāng)前余額。通過重播這些事件可以得出當(dāng)前余額。
CQRS 和事件溯源通過以下方式相互補(bǔ)充:
如前所述,Axon 框架提供了一種在 Spring 應(yīng)用程序中實(shí)現(xiàn) CQRS 和事件源的無縫方法:
聚合和事件處理: 在 Axon 中,聚合負(fù)責(zé)命令處理和事件生成。處理命令后,它們應(yīng)用導(dǎo)致狀態(tài)更改的事件。
@Aggregate
public class Account {
@AggregateIdentifier
private String accountId;
private int balance;
@CommandHandler
public void handle(WithdrawMoneyCommand cmd) {
if (cmd.getAmount() > balance) {
throw new InsufficientFundsException();
}
apply(new MoneyWithdrawnEvent(cmd.getAccountId(), cmd.getAmount()));
}
@EventSourcingHandler
public void on(MoneyWithdrawnEvent evt) {
this.balance -= evt.getAmount();
}
}事件存儲: Axon 提供了一種存儲和檢索事件的機(jī)制。可以重播這些事件以重建聚合的狀態(tài)。
投影: Axon 中的投影提供了 CQRS 的查詢端。他們監(jiān)聽事件并更新讀取優(yōu)化視圖。這樣,您的查詢模型始終會根據(jù)最新更改保持更新。
雖然 CQRS 和事件溯源可以提供巨大的好處,但它們也具有復(fù)雜性。意識到這些挑戰(zhàn)將確保更明智、更順利的實(shí)施。
架構(gòu)復(fù)雜性: CQRS 和事件源向系統(tǒng)引入了額外的層和組件,例如事件存儲、命令和事件總線以及同步機(jī)制。
學(xué)習(xí)曲線: 對于剛接觸這些模式的團(tuán)隊(duì),將有一個學(xué)習(xí)階段。從傳統(tǒng)的基于 CRUD 的系統(tǒng)的概念轉(zhuǎn)變可能具有挑戰(zhàn)性。
最終一致性: 鑒于命令和查詢模型的隔離性質(zhì),通常會為了最終一致性而犧牲即時一致性。這意味著在命令端所做的更改反映在查詢端之前可能會有延遲。
事件排序: 確保事件按照生成的順序進(jìn)行處理,尤其是在分布式系統(tǒng)中,可能很棘手,但對于保持一致的狀態(tài)至關(guān)重要。
隨著時間的推移,事件的結(jié)構(gòu)或語義可能會發(fā)生變化,從而導(dǎo)致以下挑戰(zhàn):
存儲注意事項(xiàng): 由于存儲所有事件,事件存儲可能會快速增長,從而導(dǎo)致存儲成本增加和潛在的性能問題。
重播持續(xù)時間: 通過重播長期歷史事件來重建系統(tǒng)狀態(tài)可能非常耗時,會影響系統(tǒng)恢復(fù)和初始化時間。
將使用 CQRS 和事件源的系統(tǒng)與不遵循這些模式的外部系統(tǒng)集成可能具有挑戰(zhàn)性,特別是在數(shù)據(jù)同步和事務(wù)管理方面。
粒度決策: 決定應(yīng)用 CQRS 和事件溯源的粒度至關(guān)重要。在微觀層面上實(shí)施它們可能會導(dǎo)致過于復(fù)雜化,而過于廣泛地實(shí)施可能會削弱好處。
域復(fù)雜性: 這些模式對于簡單域來說可能有點(diǎn)過分了。它們更適合復(fù)雜的領(lǐng)域,其好處超過了實(shí)施和維護(hù)成本。
雖然有像 Axon 和框架這樣的工具支持 CQRS 和事件溯源,但它們可能并不總是適合所有場景??赡苄枰远x實(shí)現(xiàn),這會增加項(xiàng)目的復(fù)雜性和持續(xù)時間。
CQRS 提供了一種獨(dú)特的方式來擴(kuò)展和組織微服務(wù)。當(dāng)與 Spring 的生態(tài)系統(tǒng)結(jié)合時,它可以提供強(qiáng)大的工具包來構(gòu)建健壯、可擴(kuò)展和可維護(hù)的系統(tǒng)。然而,與所有架構(gòu)決策一樣,必須權(quán)衡利弊并確保它適合您的特定用例。

我們在微信上24小時期待你的聲音
解答本文疑問/技術(shù)咨詢/運(yùn)營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流