Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >DDD - 聚合与聚合根_如何理解 Respository与DAO

DDD - 聚合与聚合根_如何理解 Respository与DAO

作者头像
小小工匠
发布于 2022-01-12 06:56:47
发布于 2022-01-12 06:56:47
97300
代码可运行
举报
文章被收录于专栏:小工匠聊架构小工匠聊架构
运行总次数:0
代码可运行

文章目录


Pre

通常情况,我们都会面临这样的一个问题: 架构图说的是一回事,代码说的却是另一回事 。 当然了这里面的影响因素很多,有一个原因就是某些约束没有在设计中体现出来,也就是说设计的表现力不够 , 而这些约束需要阅读代码才能够知道,这就增加了理解和使用这个组件的难度。

这个问题在基于数据建模的设计方法上比较明显, 举个例子:

DDD - 如何理解Entity与VO提到的购物场景 ,我们以数据驱动的方式来设计订单和产品表,

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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是一对多的关系。


代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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之间是一对多的关系


Question

Q: order与order_detail之间的关系与product与product_comment之间的关系是一样的吗 ?

这mmp, 单单从数据模型上完全区分不出来啊 ,那只能看下业务代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@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 {
	 // 保存产品
   }
 }
}
  • 订单和订单明细是一起保存的,也就是说两者可以作为一个整体来看待 (这个整体就是我们说的聚合)
  • 产品和产品评论之间并不能被看做一个整体,所以没有在一起进行操作

这层逻辑,光看上面的设计是看不出来的,只有看到代码了,才能理清这一层关系 , 无形中就增加了理解和使用难度。

「聚合」就是缓解这种问题的一种手段!


如何理解 聚合和聚合根

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Artisan {
 public void say() {
	 System.out.println("1");
	 System.out.println("2");
 }
}

对于上面的代码,如何保障在多线程情况下1和2能按顺序打印出来?最简单的方法就是使用synchronized关键字进行加锁操作

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Artisan {
 public synchronized void say() {
 	System.out.println("1");
	 System.out.println("2");
 }
}

synchronized保证了代码的原子性执行. 就像 事务保证了原子性操作一样。

但是,这和「聚合」有什么关系呢?

如果说,synchronized是多线程层面的锁;事务是数据库层面的锁,那么「聚合」就是业务层面的锁!

在业务逻辑上,有些对象需要保持操作上的原子性,否则就没有任何意义。这些对象就组成了「聚合」!


利用聚合解决业务上的原子性操作

对于上面的订单与订单详情,从业务上来看,订单与订单明细需要保持业务上的原子性操作

  • 订单必须要包含订单明细
  • 订单明细必须要属于某个订单
  • 订单和订单明细被视为一个整体,少了任何一个都没有意义

所以其对象模型可以表示为:

  • 订单和订单明细组成一个「聚合」
  • 订单是操作的主体,所以订单是这个「聚合」的「聚合根」
  • 所有对这个「聚合」的操作,只能通过「聚合根」进行

相应的,产品和产品评价就不构成「聚合」。虽然在表设计时,订单和订单明细的结构关系与产品与产品评价的结构关系是一样的!因为:

  • 虽然产品评价需要属于某个产品
  • 但是产品不一定就有产品评价
  • 产品评价可以独立操作

所以产品与产品评论的模型则可以表示为:

  • 产品和产品评论是两个「聚合」
  • 产品评论通过productId与「产品聚合」进行关联

如何确定聚合和聚合根

对象在业务逻辑上是否需要保证原子性操作是确定聚合和聚合根的其中一个约束。

还有一个约束就是「边界」,即聚合多大才合适?过大的「聚合」会带来各种问题。

还是以锁举例,看下面的代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Artisan{
 public synchronized void say() {
	 System.out.println("0");
	 System.out.println("1");
	 System.out.println("2");
	 System.out.println("4");
  }
}

只希望12能按顺序打印出来,而0和4没有这个要求!上面的代码能满足要求,但是影响了性能。优化方式是使用同步块,缩小同步范围:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Artisan{
 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 VS DAO

在理解了聚合之后,就可以很容易的区分Respository与DAO了

  • DAO是技术手段,Respository是抽象方式
  • DAO只是针对对象的操作,而Respository是针对「聚合」的操作

【DAO的操作方式】

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@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的操作方式】

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 订单和订单明细构成聚合
public clas  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操作甚至内存操作。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022/01/12 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Spring Cloud【Finchley】实战-02订单微服务
通常来讲,微服务都是分数据库的。这里我们新建个数据库给订单微服务 ,数据库实例名 o2o-order
小小工匠
2021/08/17
3720
(2) 电商数据库表设计
用户数据库(mc_userdb):用户信息表(customer_inf)、用户登录表(customer_login)、 用户级别表(customer_level_inf)、用户积分日志表(customer_point_log)、用户余额变动表(customer_balance_log)、 用户登录日志表(customer_login_log) 商品数据库(mc_productdb):品牌信息表(product_brand_info)、商品分类表(product_category)、供应商信息表(product_supplier_info)、商品信息表(product_info)、商品图片信息表(product_pic_info)、商品评论表(product_comment) 订单数据库(mc_orderdb):订单主表(order_master)、订单详情表(order_detail)、用户地址表(customer_addr)、仓库信息表(warehouse_info)、物流公司信息表(shipping_info)、 购物车表(order_cart)
用户1214487
2022/03/26
7560
Spring Cloud【Finchley】实战-03订单微服务与商品微服务之间的调用
访问 http://localhost:8081/order/getServerInfoFromClient
小小工匠
2021/08/17
5540
SpringBoot的微信点餐系统后台开发要点
项目设计 角色划分 买家(微信端) 卖家(PC端) 功能分析 关系 部署架构 架构和基础框架 演进:单一应用架构->垂直应用架构->分布式服务架构->流动计算架构 国内微服务门派: 阿里系: Du
linxinzhe
2018/04/10
3K0
SpringBoot的微信点餐系统后台开发要点
关于聚合根、领域事件的那点事——深入浅出理解DDD
Tech 导读 领域驱动设计(Domain-Driven Design,简称DDD)是一种软件开发方法论,旨在帮助开发人员更好地理解和解决复杂领域的问题。在DDD中,聚合根和领域事件是两个核心概念,它们在设计和实现领域模型时起到了重要的作用。本文将通过简单的举例方式,深入浅出地介绍聚合根和领域事件,帮助读者更好地理解DDD的核心思想和实践方法。希望本文能够为读者提供有价值的知识和启发,帮助大家在软件开发中更好地应用DDD的思想和方法。
京东技术
2023/08/22
1.4K0
关于聚合根、领域事件的那点事——深入浅出理解DDD
常见电商项目的数据库表设计(MySQL版)
简介: 目的: 电商常用功能模块的数据库设计 常见问题的数据库解决方案 环境: MySQL5.7 图形客户端,SQLyog Linux 模块: 用户:注册、登陆 商品:浏览、管理 订单:生成、管理 仓
linxinzhe
2018/07/25
51K0
常见电商项目的数据库表设计(MySQL版)
mybatis使用(配置入门)
写好mapper接口(相当于dao的接口)和mapper.xml映射文件,mybatis可以自动生成mapper接口的实现类对象
leobhao
2022/06/28
3280
9. 数仓开发之 DWD 层
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/153233.html原文链接:https://javaforall.cn
全栈程序员站长
2022/09/13
6310
(5) 电商场景下的常见业务SQL处理
可以看到possible_keys、key、key_len的值均为NULL,说明这条SQL在product_comment 表上是没有可用的索引的,取出9593行过滤度为1%
用户1214487
2022/03/26
7550
(5) 电商场景下的常见业务SQL处理
springcloud3-服务到服务调用ribbon及openfeign
1,课程回顾 2,本章重点 ribbon (负载均衡器)如何实现服务到服务的调用 feign 服务到服务的调用 3,具体内容 3.1 ribbon 3.1.1 概念 Ribbon是一种客户端负载平衡器,可让您对HTTP和TCP客户端的行为进行大量控制(借助spring封装类RestTemplate,所有的入参,请求URL及出参数都是自己配置)。Feign已使用Ribbon,因此,如果使用@FeignClient,则本节也适用。 Ribbon中的中心概念是指定客户的概念。每个负载均衡器都是组件的一部分,这些组件可以一起工作以按需联系远程服务器,并且该组件具有您作为应用程序开发人员提供的名称(指定远程调用的服务名称,例如,使用@FeignClient批注)。根据需要,Spring Cloud通过使RibbonClientConfiguration为每个命名的客户端创建一个新的集合作为ApplicationContext。其中包含ILoadBalancer,RestClient和ServerListFilter。
张哥编程
2024/12/13
1240
springcloud3-服务到服务调用ribbon及openfeign
ES度量聚合(ElasticSearch Metric Aggregations)
本篇重点介绍Elasticsearch Metric Aggregations(度量聚合)。
丁威
2019/06/10
1.5K0
实时数仓|基于Flink1.11的SQL构建实时数仓探索实践
实时数仓主要是为了解决传统数仓数据时效性低的问题,实时数仓通常会用在实时的OLAP分析、实时的数据看板、业务指标实时监控等场景。虽然关于实时数仓的架构及技术选型与传统的离线数仓会存在差异,但是关于数仓建设的基本方法论是一致的。本文会分享基于Flink SQL从0到1搭建一个实时数仓的demo,涉及数据采集、存储、计算、可视化整个处理流程。通过本文你可以了解到:
Spark学习技巧
2020/09/08
1.9K0
实时数仓|基于Flink1.11的SQL构建实时数仓探索实践
RabbitMQ学习笔记(二)——RabbitMQ快速上手
使用微服务系统,组件之间充分解耦 使用消息中间件,解耦业务逻辑 使用数据库,持久化业务数据
不愿意做鱼的小鲸鱼
2022/09/26
5690
RabbitMQ学习笔记(二)——RabbitMQ快速上手
一波操作搞定MyCat
本文以实际操作为主,理论性比较少,主要包含mycat安装,mysql主从复制,mycat垂直分库,mycat水平分表等系列操作,希望对有机会接触到mycat的朋友一些启发。
用户3467126
2020/05/17
1K0
day62_Mybatis学习笔记_02
(1)创建扩展PO类   一般User.java类要和数据表表字段一致,最好不要在这里面添加其他字段,今天学习mybatis的逆向工程时,会根据表结构,生成po类,如果在po类中扩展字段,此时会被覆盖掉。   所以针对要扩展的po类,我们需要创建一个扩展类,来继承它。
黑泽君
2018/11/09
1K0
Mybatis笔记二
注意:因为一个订单信息只会是一个人下的订单,所以从查询订单信息出发关联查询用户信息为一对一查询。如果从用户信息出发查询用户下的订单信息则为一对多查询,因为一个用户可以下多个订单。
HUC思梦
2020/09/03
4950
Mybatis笔记二
后端开发实践系列——领域驱动设计(DDD)编码实践
的确,很多时候软件的业务逻辑是无法通过推理而得到的,有时甚至是被臆想出来的。这样的结果使得原本已经很复杂的业务变得更加复杂而难以理解。而在具体编码实现时,除了应付业务上的复杂性,技术上的复杂性也不能忽略,比如我们要讲究技术上的分层,要遵循软件开发的基本原则,又比如要考虑到性能和安全等等。
ThoughtWorks
2019/08/01
1.3K0
后端开发实践系列——领域驱动设计(DDD)编码实践
订单微服务(5)-1024电商平台项目技术选择和创 建聚合工程项目【工业级PaaS云平台+SpringCloudAlibaba+JDK11综合项目实战】
第二十章 1024电商平台-订单微服务开发 第1集 1024电商平台-订单微服务功能需求介绍 简介:订单微服务功能需求介绍 核心接口-下单 订单微服务涉及的功能知识点 创建订单和防重提交 多个微服务之间通讯-分布式事务选择 商品库存锁定和回收 优惠券使用锁定和回收 支付宝支付对接 优惠券+商品订单验价 多通道支付和设计整合 第2集 1024电商平台-订单微服务数据库表讲解 简介:订单微服务数据库表讲解 订单表 CREATE TABLE `product_order` ( `id` bigint(11
高大北
2022/09/16
1.1K0
ORM哪家强?java,c#,php,python,go 逐一对比, 网友直呼:全面客观
为了让这个库更好用,我比较研究了各语言的主流ORM库,发现有一些语言的ORM库确实很好用,而有另外一些语言的库那不是一般的难用。
程序员汤汤
2023/01/29
2.8K2
ORM哪家强?java,c#,php,python,go 逐一对比, 网友直呼:全面客观
"氮气加速器"- 同步物化视图,让查询速度"飞"起来
深夜凌晨两点,数据工程师小张还在加班处理各种复杂的数据分析需求。"这个查询速度也太慢了,都等了半天了还没出结果..." 他揉了揉疲惫的双眼,忍不住吐槽。这不,又收到产品经理的"红色感叹号"消息:"那个实时大屏怎么又卡住了?"
一臻数据
2024/12/24
550
"氮气加速器"- 同步物化视图,让查询速度"飞"起来
推荐阅读
相关推荐
Spring Cloud【Finchley】实战-02订单微服务
更多 >
领券
💥开发者 MCP广场重磅上线!
精选全网热门MCP server,让你的AI更好用 🚀
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验