DDD - 聚合与聚合根_如何理解 Respository与DAO
文章目录
- Pre
- Question
- 如何理解 聚合和聚合根
- 利用聚合解决业务上的原子性操作
- 如何确定聚合和聚合根
- Respository VS DAO
Pre
通常情况,我们都会面临这样的一个问题: 架构图说的是一回事,代码说的却是另一回事 。 当然了这里面的影响因素很多,有一个原因就是某些约束没有在设计中体现出来,也就是说设计的表现力不够 , 而这些约束需要阅读代码才能够知道,这就增加了理解和使用这个组件的难度。
这个问题在基于数据建模的设计方法上比较明显, 举个例子:
DDD - 如何理解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之间是一对多的关系
Question
Q: order与order_detail之间的关系与product与product_comment之间的关系是一样的吗 ?
这mmp, 单单从数据模型上完全区分不出来啊 ,那只能看下业务代码
@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 {// 保存产品}}
}
- 订单和订单明细是一起保存的,也就是说两者可以作为一个整体来看待 (这个整体就是我们说的聚合)
- 产品和产品评论之间并不能被看做一个整体,所以没有在一起进行操作
这层逻辑,光看上面的设计是看不出来的,只有看到代码了,才能理清这一层关系 , 无形中就增加了理解和使用难度。
「聚合」就是缓解这种问题的一种手段!
如何理解 聚合和聚合根
public class Artisan {public void say() {System.out.println("1");System.out.println("2");}
}
对于上面的代码,如何保障在多线程情况下1和2能按顺序打印出来?最简单的方法就是使用synchronized关键字进行加锁操作
public class Artisan {public synchronized void say() {System.out.println("1");System.out.println("2");}
}
synchronized保证了代码的原子性执行. 就像 事务保证了原子性操作一样。
但是,这和「聚合」有什么关系呢?
如果说,synchronized是多线程层面的锁;事务是数据库层面的锁,那么「聚合」就是业务层面的锁!
在业务逻辑上,有些对象需要保持操作上的原子性,否则就没有任何意义。这些对象就组成了「聚合」!
利用聚合解决业务上的原子性操作
对于上面的订单与订单详情,从业务上来看,订单与订单明细需要保持业务上的原子性操作:
- 订单必须要包含订单明细
- 订单明细必须要属于某个订单
- 订单和订单明细被视为一个整体,少了任何一个都没有意义
所以其对象模型可以表示为:
- 订单和订单明细组成一个「聚合」
- 订单是操作的主体,所以订单是这个「聚合」的「聚合根」
- 所有对这个「聚合」的操作,只能通过「聚合根」进行
相应的,产品和产品评价就不构成「聚合」。虽然在表设计时,订单和订单明细的结构关系与产品与产品评价的结构关系是一样的!因为:
- 虽然产品评价需要属于某个产品
- 但是产品不一定就有产品评价
- 产品评价可以独立操作
所以产品与产品评论的模型则可以表示为:
- 产品和产品评论是两个「聚合」
- 产品评论通过productId与「产品聚合」进行关联
如何确定聚合和聚合根
对象在业务逻辑上是否需要保证原子性操作是确定聚合和聚合根的其中一个约束。
还有一个约束就是「边界」,即聚合多大才合适?过大的「聚合」会带来各种问题。
还是以锁举例,看下面的代码
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没有这个要求!上面的代码能满足要求,但是影响了性能。优化方式是使用同步块,缩小同步范围:
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的操作方式】
@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的操作方式】
// 订单和订单明细构成聚合
public clas Order{List<OrderDetail> itemLine; // 这里就保证了设计与编码的一致性...
}@Service
@Transactional
public class OrderService {public void createOrder(Order order) throws Exception {orderRespository.save(order);//ororder.save(); // 内部调用orderRespository.save(this);}
}
当然,orderRespository的save方法中,可能还是数据库相关操作,但也可能是NoSql操作甚至内存操作。
DDD - 聚合与聚合根_如何理解 Respository与DAO相关推荐
- 深入理解DDD中的聚合
本文来说下领域驱动设计中的聚合 文章目录 概述 聚合解决的核心问题是什么 聚合划分的原则 生命周期一致性 问题域一致性 场景频率一致性 尽量小的聚合 实现方面的考虑 资源库.工厂面向聚合定义 代码结构 ...
- DDD中聚合、聚合根的含义以及作用
一. 聚合与聚合根的含义 1. 聚合 聚合往往是一些实体为了某项业务而聚类在一起形成的集合 ,举个例子,社会是由一个个的个体组成的,象征着我们每一个人.随着社会的发展,慢慢出现了社团.机构.部门等组织 ...
- ASP.NET Core Web API下事件驱动型架构的实现(四):CQRS架构中聚合与聚合根的实现
在前面两篇文章中,我详细介绍了基本事件系统的实现,包括事件派发和订阅.通过事件处理器执行上下文来解决对象生命周期问题,以及一个基于RabbitMQ的事件总线的实现.接下来对于事件驱动型架构的讨论,就需 ...
- 基于ABP落地领域驱动设计-02.聚合和聚合根的最佳实践和原则
前言 上一篇 基于ABP落地领域驱动设计-01.全景图 概述了DDD理论和对应的解决方案.项目组成.项目引用关系,以及基于ABP落地DDD的通用原则.从这本篇开始,会更加深入地介绍在基于 ABP Fr ...
- 基于花季A传媒ABP落地领域驱动设计-02.聚合和聚合根的最佳实践和原则
DDD和ABP Framework两个中心技术,后边还会陆续发布中心构件完成.归纳事例完成系列文章,敬请关注! ABP Framework 学习及实施DDD经历分享:示例源码.电子书同享,欢迎加入 ...
- 领域驱动设计之聚合与聚合根实例一
通过一个实例来说明如何划分聚合与聚合根 场景:一个下订单的业务,一个订单必须有相应的客户信息,订单下有订单项,每个订单项必须有相应的产品信息,产品有分类的信息. 1.根据这个基本的需求,我们初步确定的 ...
- cisco链路聚合 不均衡_思科CISCO交换机间链路聚合端口聚合实现方法详解
本文讲述了思科CISCO交换机间链路聚合端口聚合实现方法.分享给大家供大家参考,具体如下: [CISCO] 交换机间链路聚合端口聚合 端口通道( port channel ) 是一种聚合多个物理接口 ...
- java 聚合_Java聚合
聚合 如果一个类有一个类的实体引用(类中的类),则它称为聚合. 聚合表示HAS-A关系. 考虑有一种情况,Employee对象包含许多信息,例如:id,name,emailId等.它包含另一个类对象: ...
- ElasticSearch java API - 聚合查询-聚合多字段聚合demo
以球员信息为例,player索引的player type包含5个字段,姓名,年龄,薪水,球队,场上位置. index的mapping为: "mappings": {"pl ...
最新文章
- 【2020新书推荐】Introduction to Deep Learning
- centos7 禁止ip访问_centos7 防火墙操作 屏蔽ip
- 在 2016 年学 JavaScript 是一种什么样的体验?
- linux手机远程桌面连接软件下载,HomeCenter手机远程桌面下载_HomeCenter手机远程桌面官方下载-太平洋下载中心...
- Nginx负载均衡状态介绍
- AutoRest - 具有 C# 和 Razor 模板的 Swagger 规范代码生成器。
- SAP License:给SAP顾问的5个小贴士
- 【Oracle】RMAN备份
- weex android 交互,weex项目接入到Android studio中
- CodeIgniter框架中的多语言
- 第一课 Delphi7完全自学教程
- 机器学习11种优化器推导过程详解(SGD,BGD,MBGD,Momentum,NAG,Adagrad,Adadelta,RMSprop,Adam,Nadma,Adamx)
- [渝粤教育] 西南科技大学 行政法学与行政诉讼法学 在线考试复习资料(1)
- verilog REG 寄存器、向量、整数、实数、时间寄存器
- oracle 范鑫_快速理解数据库中的索引(Indexes in Database)
- 无法引用Microsoft.Office.Interop.Excel的解决
- axios获取html页面,axios 获取后台数据
- Diango:Django基础
- 电商环境下中小企业客户关系管理系统设计
- 不会Ps?没关系,这些在线平面设计网站,很实用
热门文章
- java获取屏幕图像_Java捕获当前屏幕图像
- 洛谷P1040 加分二叉树运用区间DP(动态规划)求解
- 数字图像处理——第四章 频率域图像增强
- oracle数据抽取脚本,oracle的一些信息抽取脚本.sql
- c++ 纯虚函数和抽象类那些事(三)
- 如何评价三国里的袁绍
- sql array 数组基本用法(四)
- Python 非线性方程组
- vb表格控件_(超级干货)ExcelVBA拆分表格并分别发送邮件增强版
- python安装numba_python – 在OS X上安装Numba时出错