首页
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照片墙
统计
留言
搜索到
2
篇与
的结果
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-03-07
Lock 接口
Lock是显式锁(手动开启和关闭锁,别忘记关闭锁)synchronized是隐式锁,出了作用域自动释放Lock 只有代码块锁,synchronized有代码块锁和方法锁 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类) 优先使用顺序: Lock >同步代码块(已经进入了方法体,分配了相应资源)>同步方法(在方法体之外)
2023年03月07日
88 阅读
0 评论
17 点赞