广告
您当前的位置: 首页 >  技术 >  编程开发

领域建模战术:解密 CQRS(读写责任分离)的物理架构与设计

作者:CoderWang 时间:2026-07-07 阅读数:4人阅读

在开展大型企业复杂业务系统开发时,我们的数据库往往面临着截然不同的双重访问压力:一是**高吞吐、强一致的写事务**(如提交订单、扣减库存);二是**多维度、复杂的读查询**(如多表联查、模糊检索、数据分析)。

如果强行将写事务的“充血领域模型”与读查询的“多表联查 DTO”绑定在同一个物理模型和同一个数据库上,会导致领域模型充斥大量的查询噪音(Getter 暴露、贫血字段),且数据库由于索引过多导致写性能崩溃。

为了将**“写状态改变”**与**“读状态展示”**彻底剥离,领域驱动设计(DDD)推荐使用**CQRS(命令查询职责分离)**架构模式。

本文将系统拆解 CQRS 的物理隔离拓扑、双向数据模型、以及读写最终一致性流转机制。

一、 核心范式对比:单体共享模型 vs. CQRS 读写分离

CQRS 模式将系统彻底切割为“命令侧”(写模型)与“查询侧”(读模型):

特征维度传统共享模型 (Shared Model)CQRS 架构模式 (Command Query Segregation)
模型表达同一个 JPA Entity 实体既充当写操作校验,又暴露字段给读视图。**写模型 (Command Model)**:强内聚充血领域模型。**读模型 (Query Model)**:极其扁平的视图或 DTO。
数据库设计写与读共享同一个 MySQL 数据库物理表。**写数据库 (Write DB)**:规范化设计(3NF),消灭冗余。**读数据库 (Read DB)**:去规范化设计(NoSQL / ES),专为极速检索优化。
缓存一致性需要复杂的缓存淘汰与回填代码。通过**领域事件(Domain Event)**驱动异步刷新,达成最终一致。
高并发表现读与写事务互相争抢表锁与行锁,吞吐率受限。**读写彻底解耦**。写节点与读节点可独立水平扩展。
---

二、 CQRS 读写物理分库与事件同步流转拓扑

在高级 CQRS 架构中,读写通常不仅是代码类分离,更会进一步演进为**物理数据库的分离**:

      [ 客户端业务写命令 (Command) ]               [ 客户端数据读请求 (Query) ]
                     │                                            │
                     ▼ 1. 发起写操作                               ▼ 1. 发起复杂多表联查
          [ 写模型服务 (Command Context) ]             [ 读模型服务 (Query Context) ]
                     │                                            │
                     ├─ 2. 写入物理库: Write DB (MySQL)           ├─ 2. 查询物理库: Read DB (Elasticsearch / Redis)
                     ├─ 3. 本地事务内保存并抛出领域事件            │
                     │                                            ▼ 3. 返回极扁平数据 DTO
                     ▼ 4. 异步发送事件广播
        [ 消息队列 (Kafka / RocketMQ) ]
                     │
                     ▼ 5. 监听事件并消费更新
        [ 读库同步消费者 (ReadSyncConsumer) ] ── 6. 强回填 ──> [ Read DB (同步完成) ]
---

三、 代码实战:在应用服务层编排 CQRS 的写与读逻辑

以下展示了如何在应用层,干净地将写命令(Command)与读查询(Query)分流处理,防止读查询逻辑侵入领域层:

1. 写模型侧:应用服务处理命令(Command Side)

package com.company.sales.command.application;

import com.company.sales.command.domain.SalesOrder;
import com.company.sales.command.domain.repository.OrderRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class OrderCommandApplicationService {

    private final OrderRepository orderRepository; // 仅面向写模型的仓储

    public OrderCommandApplicationService(OrderRepository repository) {
        this.orderRepository = repository;
    }

    /**
     * 写用例:付款(严格走充血领域模型不变性校验)
     */
    @Transactional
    public void payOrder(String orderId) {
        SalesOrder order = orderRepository.findById(orderId);
        order.pay(); // 执行状态变更并隐式积累领域事件
        orderRepository.save(order); // 保存并自动广播事件以刷新读库
    }
}

2. 读模型侧:直接调用极简 DAO 拉取查询视图(Query Side)

package com.company.sales.query.application;

import com.company.sales.query.dao.OrderQueryDAO;
import com.company.sales.query.dto.OrderSearchViewDTO;
import org.springframework.stereotype.Service;
import java.util.List;

@Service
public class OrderQueryApplicationService {

    private final OrderQueryDAO orderQueryDao; // 直连读库的只读 DAO

    public OrderQueryApplicationService(OrderQueryDAO queryDao) {
        this.orderQueryDao = queryDao;
    }

    /**
     * 读用例:多维复合检索(不经过任何领域模型,直接返回扁平 DTO)
     */
    public List<OrderSearchViewDTO> searchOrders(String customerId, String status, int page, int size) {
        // 直接从 Elasticsearch 或 MySQL 只读从库中拉取完全拼装好的扁平视图数据
        return orderQueryDao.search(customerId, status, (page - 1) * size, size);
    }
}
---

四、 总结

CQRS 模式是微服务架构应对超大流量、复杂数据查询场景的“破局战术方案”。

它通过**在架构层将修改(Command)与查询(Query)物理截断,消除了领域模型充当“万能查询对象”的技术死角,释放了领域层的高表达力;并结合异步事件刷新读库,达成了读写资源的按需水平扩展**。掌握这套 CQRS 物理隔离拓扑与读写代码分流规范,是治理复杂遗留系统查询淤积、主导高并发电商及金融核心重构设计的核心看家本领!

本站所有文章、数据、图片均来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。

评论交流 (0)

正在加载评论...
头像

CoderWang

当你还撑不起你的梦想时,就要去奋斗。如果缘分安排我们相遇,请不要让她擦肩和过。我们一起奋斗!

微信