首页
3D照片墙
统计
留言
Search
1
1.OAuth 的简单理解
115 阅读
2
多个拦截器的执行顺序
105 阅读
3
基于Annotation方式的声明式事务
102 阅读
4
6.设计模式汇总
101 阅读
5
Unity 依赖注入
98 阅读
Java
JDBC
Spring
Spring MVC
SpringBoot
SpringCloud
MybatisPlus
Mybatis
Maven
SpringSecurity
JVM
java注解与反射
Java JUC并发编程
SSM
.NET
IdentityServer4
EF
.Net Core
AbpVNext + DDD
.NET MVC Api
前端
Jquery&JavaScript
uniapp
VUE
Echars
Vue底层原理
Python
Django
软考笔记
软件设计师
1.计算机组成与体系结构
10.面向对象技术
11.UML类图建模
12.面向对象程序设计
13.数据结构
14.算法基础
16.知识产权标准化
17.程序设计语言
2.操作系统
3.数据库
4.数据库设计
5.计算机网络
6.信息安全
7.系统开发基础
8.项目管理
9.数据流图
架构设计
CQRS架构
DDD架构
数据库技术
SQL锁
SqlServer
Oracle 主从备份
Oracle RAC集群
Mysql
云原生/容器技术
kubernetes
Docker
数据结构与算法
常用中间件
Redis
RabbitMQ 消息队列
ElasticSearch
其他
PHP
OAuth 2.0
WebSocket
ArkTs Harmony 开发
运维
Search
标签搜索
排序算法
vue
算法
遍历
docker
线性
数组
dom
synchronized
数据库
xml语言
log4j
bigint
静态函数
静态方法
哈夫曼树
const
冒泡排序
商标设计
命令模式
Bi8bo
累计撰写
304
篇文章
累计收到
6
条评论
首页
栏目
Java
JDBC
Spring
Spring MVC
SpringBoot
SpringCloud
MybatisPlus
Mybatis
Maven
SpringSecurity
JVM
java注解与反射
Java JUC并发编程
SSM
.NET
IdentityServer4
EF
.Net Core
AbpVNext + DDD
.NET MVC Api
前端
Jquery&JavaScript
uniapp
VUE
Echars
Vue底层原理
Python
Django
软考笔记
软件设计师
1.计算机组成与体系结构
10.面向对象技术
11.UML类图建模
12.面向对象程序设计
13.数据结构
14.算法基础
16.知识产权标准化
17.程序设计语言
2.操作系统
3.数据库
4.数据库设计
5.计算机网络
6.信息安全
7.系统开发基础
8.项目管理
9.数据流图
架构设计
CQRS架构
DDD架构
数据库技术
SQL锁
SqlServer
Oracle 主从备份
Oracle RAC集群
Mysql
云原生/容器技术
kubernetes
Docker
数据结构与算法
常用中间件
Redis
RabbitMQ 消息队列
ElasticSearch
其他
PHP
OAuth 2.0
WebSocket
ArkTs Harmony 开发
运维
页面
3D照片墙
统计
留言
搜索到
9
篇与
的结果
2024-12-12
什么是CQRS架构(命令查询职责分离架构)
目录 一、什么是CQRS 二、为什么要用CQRS 三、CQRS的使用 四、CQRS的好处 五、CQRS的缺点 六、何时使用CQRS 总结 一、什么是CQRS Greg Young 在 2010 年创造了CQRS(Command Query Responsibility Segregation)架构模式。 CQRS是基于CQS,CQS (命令查询分离)设计模式建议将对象的方法映射到两类:方法要么改变对象的内部状态,但不返回任何内容,要么只返回元数据。这种方法称为Command。或者一个方法返回信息但不改变内部状态。这种方法称为Query。 命令查询职责分离(Command Query Responsibility Segregation,CQRS),是一种读写分离模式,旨在从业务上分离命令(Command,也就是写操作)和查询(Query,读操作)的行为。从而使得逻辑更加清晰,便于开发人员对不同部分进行针对性的优化。 由CQS演变而来的CQRS 的核心思想是将这两类不同的操作进行分离,然后在两个独立的service中实现。这里的service一般是指两个独立部署的应用。在某些特殊情况下,也可以部署在同一个应用内的不同接口上。 二、为什么要用CQRS 传统的开发方式,就是读写都是用的同一个模型。如下图所示,请求和返回都是DTO。一个DTO模型就表达了用户所有的CURD的意图了。而且为了适应同时适应查询和创建操作,DTO被设计的面面俱到,也就显得臃肿。从而在传输中存在不必要的字段传递。 有的同学就会问了,那我数据库不也有读写分离吗,你这CQRS不会是新瓶装老酒,拿个新名词来忽悠人的吧。且慢,容我慢慢道来。如果是在传统的开发方式,就算你是数据库读写分离了,你用的数据模型还是同一个,如下图所示。而且每次操作,在DTO与领域对象间进行多次转换,增加了系统复杂度。即使你数据库读写分离了,但是读写模型没有拆分的话,服务的并发瓶颈还是在那里。 在实际应用场景中,读取和写入工作负荷通常是非对称的,具有截然不同的性能和缩放要求。正常都是读多写少,同时读取的接口有各式各样的参数回显需求。而写入的话,一般的数据都是比较固定的。同时数据查询都不会改变数据库的数据。传统开发方式的读写操作围绕同一数据模型展开,对于读多写少的系统而言效率并不是最高的,特别在读操作为主的高并发系统中缺点就尤为突出。 三、CQRS的使用 鉴于传统开发模式的高并发短板,因此,我们把读写模型进行拆分。CQRS根据读写职责的不同,将读取和写入分离到不同的模型,使用命令来更新数据,使用查询来读取数据。这时的模型如下图所示。我们的command model对应到写数据库,query model对应到了读数据库。此时,数据库的读写分离。 红色线部分就是Command端,其对应的是Command Model对其发送Command操作的指令向写数据库中写入数据。 绿色线部分是Query端作为查询操作,通过Query Model向读数据库获取数据,通过蓝色向左的线逐层把数据返回结果给Client。 四、CQRS的好处 CQRS的好处,读写分离,解决的就是复杂软件系统中的读多写少的问题。 因为读和写的参数不一样,因此要分别定义写接口的参数,和读接口的参数。 写接口一般比较少变动,可以遵守范式来写。 读接口根据业务相对会变动比较多,而且有时候需要直接跨层调用,如从Application层直接调用Infrastructure层。 CQRS很适合基于事件的编程模型。CQRS系统被分割成独立的服务,与事件协作进行通信。这使得这些服务可以轻松地利用事件溯源(Event Sourcing)的优势。 五、CQRS的缺点 凡事有利必有弊,使用CQRS,由于读写的模型分离了,首先从代码量上面肯定增加了,更重要的一点便是增加了软件系统的复杂。 CQRS也算是一种开发模式,一种新思想的引入也提高了程序员的开发门槛。 六、何时使用CQRS 以下情况建议使用CQRS模式: 首先便是正文中提到的读多写少的场景。这个时候可以考虑使用CQRS。 读写性能分别的要求,数据读取性能必须独立于数据写入性能进行优化的场景。可以对读和写两方面应用不同的优化策略 以下情况不建议使用此模式: 域或业务规则非常简单。 简单的 CRUD 样式用户界面和数据访问操作就足够了。 总结 本文简单的介绍了CQRS是什么,为啥要用,以及优缺点。在实际使用过程中,CQRS往往会结合事件溯源Event Sourcing一起使用的。这个在本篇文章里没有详细展开,有兴趣的同学可以自行搜索资料进行拓展阅读,我也会在后面整理相关资料,以及配上对应的工程实例帮助大家理解。
2024年12月12日
43 阅读
0 评论
8 点赞
2024-05-14
知乎
阿里技术专家详解DDD系列 第二弹 - 应用架构 - 知乎 (zhihu.com) Air433/dddbook: spring boot 领域驱动设计Demo (github.com) 架构设计快速入门——领域驱动设计(DDD)(C#)_c# ddd-CSDN博客 领域驱动设计-实体、值对象和聚合 - 知乎 (zhihu.com)
2024年05月14日
35 阅读
0 评论
62 点赞
2024-03-17
1. POP,OOP,DDD三大概念
POP 面向过程编程 POP面向过程编程,事物比较简单,可以用线性的思维去解决问题。 无法应对复杂场景 OOP面向对象编程 是一种编程思想,是考虑问题的方式,可以应对复杂的业务需求, OOP的内部,依旧是POP线性思维 OOP是程序设计,POP是内部实现 - 建大厦 POP-无边界 OOP-以对象为边界(被类束缚了) DDD-扩大边界(问题域为边界),将对象组装成领域,程序分析设计更轻松 DDD是一种程序分析设计方法,不关乎具体技术具体代码实现,依旧是OOP和AOP 领域可大可小,没有一个专门的规定,领域中还是对象,对象与对象之间协调组成领域。
2024年03月17日
45 阅读
0 评论
66 点赞
2024-01-07
基本概念 - 领域设计:聚合与聚合根
什么是聚合? 什么是聚合根? 如何确定聚合和聚合根? Respository与DAO的区别 设计的表现力 《程序员必读之软件架构》一书在「软件架构和编码」一章有这么一段话: 尽管很多人以组件来谈论他们的软件系统,然而代码通常并未反映出这种结构。这就是软件架构和依据原则编码之间会脱节的原因之一:墙上的架构图说的是一回事,代码说的却是另一回事。 个人认为这是架构与代码差异的一个原因。还有一个原因就是某些约束没有在设计中体现出来,也就是说设计的表现力不够,而这些约束需要阅读代码才能够知道,这就增加了理解和使用这个组件的难度。这个问题在基于数据建模的设计方法上比较明显。 以领域设计:Entity与VO提到的淘宝购物为例,以数据驱动的方式来设计,我们会有如下两张表: CREATE TABLE `order` ( `rec_id` BIGINT(11) NOT NULL AUTO_INCREMENT COMMENT '主键', `seller_id` BIGINT(11) NOT NULL COMMENT '卖家', `buyer_id` BIGINT(11) NOT NULL COMMENT '买家', `price` BIGINT(11) NOT NULL COMMENT '订单总价格,按分计算', ... PRIMARY KEY (`rec_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ; CREATE TABLE `order_detail` ( `rec_id` BIGINT(11) NOT NULL AUTO_INCREMENT COMMENT '主键', `order_id` BIGINT(11) NOT NULL COMMENT '订单主键', `product_name` VARCHAR(50) COMMENT '产品名称', `product_desc` VARCHAR(200) COMMENT '产品描述', `product_price` BIGINT(11) NOT NULL COMMENT '产品价格,按分计算', ... PRIMARY KEY (`rec_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ; 从表关系上,我们只能知道order与order_detail是一对多的关系。我们再看下面这两张表: CREATE TABLE `product` ( `rec_id` BIGINT(11) NOT NULL AUTO_INCREMENT COMMENT '主键', `name` VARCHAR(50) COMMENT '产品名称', `desc` VARCHAR(200) COMMENT '产品描述', ... PRIMARY KEY (`rec_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ; CREATE TABLE `product_comment` ( `rec_id` BIGINT(11) NOT NULL AUTO_INCREMENT COMMENT '主键', `product_id` BIGINT(11) NOT NULL COMMENT '产品', `cont` VARCHAR(2000) COMMENT '评价内容', ... PRIMARY KEY (`rec_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ; 从表关系上,我们也只能知道product与product_comment之间是一对多的关系。 那么,请问:order与order_detail之间的关系与product与product_comment之间的关系是一样的吗?至少从上面的表设计上,完全看不出来! 我们需要深入到代码,才能够发现差异: @Service @Transactional public class OrderService { public void createOrder(Order order,List<OrderDetail> orderDetailList) throws Exception { // 保存订单 // 保存订单详情 } } } @Service @Transactional public class ProductService { public void createProduct(Product prod) throws Exception { // 保存产品 } } } 订单和订单明细是一起保存的,也就是说两者可以作为一个整体来看待(这个整体就是我们下面要说的聚合) 而产品和产品评论之间并不能被看做一个整体,所以没有在一起进行操作 这层逻辑,你光看上面的设计是看不出来的,只有看到代码了,才能理清这一层关系。这无形中就增加了理解和使用难度。「聚合」就是缓解这种问题的一种手段! 什么是聚合和聚合根? 在讨论聚合之前,我们先来看一段Java代码: public class People { public void say() { System.out.println("1"); System.out.println("2"); } } 对于上面的代码,如何保障在多线程情况下1和2能按顺序打印出来?最简单的方法就是使用synchronized关键字进行加锁操作,像这样: public class People { public synchronized void say() { System.out.println("1"); System.out.println("2"); } } synchronized保证了代码的原子性执行。与之类似的就是事务,在JDBC的架构设计中已经聊过了事务,这里不再赘述。事务保证了原子性操作。 但是,这和「聚合」有什么关系呢? 如果说,synchronized是多线程层面的锁;事务是数据库层面的锁,那么「聚合」就是业务层面的锁! 在业务逻辑上,有些对象需要保持操作上的原子性,否则就没有任何意义。这些对象就组成了「聚合」! 对于上面的订单与订单详情,从业务上来看,订单与订单明细需要保持业务上的原子性操作: 订单必须要包含订单明细 订单明细必须要属于某个订单 订单和订单明细被视为一个整体,少了任何一个都没有意义 所以其对象模型可以表示为: 订单和订单明细组成一个「聚合」 订单是操作的主体,所以订单是这个「聚合」的「聚合根」 所有对这个「聚合」的操作,只能通过「聚合根」进行 相应的,产品和产品评价就不构成「聚合」。虽然在表设计时,订单和订单明细的结构关系与产品与产品评价的结构关系是一样的!因为: 虽然产品评价需要属于某个产品 但是产品不一定就有产品评价 产品评价可以独立操作 所以产品与产品评论的模型则可以表示为: 产品和产品评论是两个「聚合」 产品评论通过productId与「产品聚合」进行关联 如何确定聚合和聚合根? 对象在业务逻辑上是否需要保证原子性操作是确定聚合和聚合根的其中一个约束。还有一个约束就是「边界」,即聚合多大才合适?过大的「聚合」会带来各种问题。 还以锁举例,看下面的代码: public class People { public synchronized void say() { System.out.println("0"); System.out.println("1"); System.out.println("2"); System.out.println("4"); } } 如果我只希望12能按顺序打印出来,而0和4没有这个要求!上面的代码能满足要求,但是影响了性能。优化方式是使用同步块,缩小同步范围: public class People { public void say() { System.out.println("0"); synchronized(Locker.class){ System.out.println("1"); System.out.println("2"); } System.out.println("4"); } } 「边界」就像上面的同步块一样,只将需要的对象组合成聚合! 假设上面的产品和产品评论构成了一个聚合!那会发生什么事情呢?当A,B两个用户同时对这个商品进行评论,A先开始评论,此时就会锁定该产品对象以及下面的所有评论,在A提交评论之前,B是无法操作这个产品对象的,显然这是不合理的。 Respository与DAO的区别 在理解了聚合之后,就可以很容易的区分Respository与DAO了: DAO是技术手段,Respository是抽象方式 DAO只是针对对象的操作,而Respository是针对「聚合」的操作 DAO的操作方式如下: @Service @Transactional public class OrderService { public void createOrder(Order order,List<OrderDetail> orderDetailList) throws Exception { Long orderId = orderDao.save(order); for(OrderDetail detail : orderDetailList) { detail.setOrderId(orderId); orderDetailDao.save(detail); } } } } 订单和和订单明细都有一个对应的DAO 订单和订单明细的关系并没有在对象之间得到体现 而Respository的操作方式如下: // 订单和订单明细构成聚合 Order{ List<OrderDetail> itemLine; // 这里就保证了设计与编码的一致性 ... } @Service @Transactional public class OrderService { public void createOrder(Order order) throws Exception { orderRespository.save(order); //or order.save(); // 内部调用orderRespository.save(this); } } 当然,orderRespository的save方法中,可能还是数据库相关操作,但也可能是NoSql操作甚至内存操作。 参考资料 《领域驱动设计:软件核心复杂性应对之道》 《实现领域驱动设计》
2024年01月07日
20 阅读
0 评论
26 点赞
2023-08-04
DDD如何拆分思路
如何落地DDD 理解领域 -> 拆分领域 -> 细化领域 电商平台为例拆分 可拆分购物领域为 秒杀,下单,登录,支付等领域 梳理领域概念 梳理出领域内我们关注的概念、概念的关系,并统一交流词汇,形成统一语言 梳理业务规则 梳理出领域内我们关注的各种业务规则,DDD中叫不变性(invariants),比如唯一性规则,余额不能小于零等 梳理业务场景 梳理出领域内的核心业务场景,比如电商平台中的加入购物车、提交订单、发起付款等核心业务场景 梳理业务流程 梳理出领域内的关键业务流程,比如订单处理流程,退款流程等
2023年08月04日
29 阅读
0 评论
11 点赞
1
2