架构设计之解析CQRS架构模式!
CQS思想
CQS
:命令和查询分离:Command and Query Segregation
。- 其核心思想是在任何一个对象的方法可以划分为两类:
- 查询:获取数据,返回查询数据,但不改变数据状态。
- 命令:改变数据状态,不返回任何数据。
- CQRS模式的核心设计理念来自于一条设计原则,即单一职责原则。
- 所谓单一职责原则,指的是一个技术组件只应该负责具体一项职责。
- 而不应该有多个导致该组件发生状态变化的操作。
- 基于
CQS
的思想,任何一个方法都可以拆分为命令和查询两部分,如下:
private int data = 0;
private int update(int value) {
data += value;
return data;
}
- 上述方法既改变了数据,又返回了数据状态。如果按照
CQS
的思想,则该方法可以拆成Command
和Query
两部分,如下:
private void update(int value) {
data += value;
}
private int query() {
return data;
}
- 对于命令侧是否返回数据实际业务诉求中并不一定能够完全统一。
- 比如:
- 某些业务场景下可能会有返回业务主键的诉求,比如下单操作返回订单号。
- 基本原则
- CQS的主要原则是:
- 一个方法要么是命令,要么是查询,但不能两者兼有。
- 这种分离有助于提高代码的可读性和维护性,因为它明确了方法的用途。
CQRS架构
Command and Query Responsibility Segregation
- 即命令查询职责分离,是一种将命令和查询的责任明确分离的架构模式。
- 这种模式进一步扩展了CQS的思想,适用于更大规模的系统架构。
- 架构思想:
- CQRS将系统的读操作和写操作分离到不同的模型中:
- 命令模型(
Command Model
):
- 处理数据的写操作(创建、更新、删除)。
- 查询模型(
Query Model
):
- 处理数据的读操作(查询)。
- 这种分离可以通过不同的数据模型、数据库甚至服务来实现,从而优化读写性能和可伸缩性。
CQRS
模式的应用非常简单,如下图所示:- 假设服务为
UserService
,在非CQRS
模式下同时包含了查询和更新服务接口。
public class UserService {
// 根据id查询用户
UserId getUserId(int userId);
// 更新用户
void updateUser(User user);
}
- 应用
CQRS
模式之后的UserService
被拆分成了两个接口,分别承担查询和写职责。
/**
命令服务
*/
public class UserCommandService {
void updateUser(UserCommand command);
}
/**
查询服务
*/
public class UserQueryService{
User getUserById(int userId);
}
- 最终一致性
- 采用
CQRS
后,查询和命令两侧通常会采用独立的数据模型。
- 采用CQRS模式并没有强制要求必须要进行数据模型的分离。
- 在这种架构模式下,命令侧的数据变化后及时同步(事件、消息队列)到查询侧,两侧数据并非实时。
- 在一定的延时后两侧数据最终达成一致。
最后总结
CQRS
的使用者可以根据实际情况,将读写分离开单独部署,然后引入领域事件,使用消息队列做通信。
- 但是这些都是基于不同业务场景的架构选择,而非
CQRS
本身的要求。 - 实际上
CQRS
只是一种非常简单的模式而已,并没有和事件、消息队列这些有强关联。
- 读写分离部署+消息通信:
- 会带来额外的系统复杂性和更高的运维成本。