17|聚合的实现(下):怎样用事务保护聚合?
- 深入了解
- 翻译
- 解释
- 总结
本文深入探讨了在软件开发中使用事务来保护聚合对象的实现过程。首先介绍了在领域层实现修改员工聚合对象的逻辑,包括添加、修改和删除技能、工作经验等方法的实现。然后详细讲解了用于修改聚合的应用服务,包括对员工聚合进行更新并保存到数据库的过程。接着,文章详细讲解了用于修改聚合的应用服务中的updator类的代码实现,包括对技能的增删改操作。最后,文章提到了对工作经验的修改类似于对技能的修改,但没有具体展开。整体来说,本文通过具体的代码示例和逻辑讲解,深入浅出地介绍了如何用事务来保护聚合对象,对于开发人员来说具有很高的参考价值。文章还讨论了乐观锁和单实体聚合的处理,为读者提供了更多的技术思考和解决问题的方法。
《手把手教你落地 DDD》,新⼈⾸单¥59
全部留言(19)
- 最新
- 精选
- Hesher回答下课后问题: 1. 业务校验可以加一个标志位判断是否是重建,重建就跳过校验; 2. 悲观锁就是分布式锁或者数据库select for update,建议用分布式锁。
作者回复: 1、是个办法。此外还可以做一个专用于重建的Builder,或者用反射, 2、这个没错
2023-04-14归属地:北京4 - 杰老师,组织Org里面包含了多个员工Emp,为什么不可以理解为组合和员工构成一个聚合,组织是聚合根,而员工是实体呢?
作者回复: 员工和组织不存在“强”的整体部分关系。比如,一个员工,可能属于多个组织,一个组织删除了,不代表下面的员工也会被删除。相反,订单和订单项是聚合。你可以体会一下。
2023-04-03归属地:广东34 - 张逃逃有个疑问想请教老师,为什么EmpRepository在查找Emp的时候不把对应Emp的所有状态(包括技能,工作经验...)全部查出来,然后通过Emp的构造参数来实例化对象,而是先实例化对象再调用addSkill()等方法来初始化,如果用构造方法来实例化对象,好像就不需要RebuiltEmp了。
作者回复: 你说的也是一种可行的做法。Evans认为,如果构造器太复杂,就掩盖了对象的主要职责,所以这时候倾向于把构造的职责抽出来。
2023-01-12归属地:北京3 - 远天老师您好,这里的查询是只查询一个员工,如果分页查询多个员工,先查询出员工,再组装每个员工的技能和经验吗?还有一种极端情况,假如员工的技能有很多,成百上千个,也要一次性查出吗,是否有性能问题?
作者回复: 好问题。 关于分页查询,参考迭代三的CQRS。 关于成百上千个技能,这时候建议不把员工和技能作为同一个聚合,而是把每个技能作为单独的聚合。
2023-03-22归属地:浙江22 - FelixsaveEmp(emp); emp.getSkills().forEach(s -> saveSkill(emp, s)); emp.getExperiences().forEach(e -> saveWorkExperience(emp, e)); emp.getEmpPosts().forEach(p -> saveEmpPost(emp, p)); 有个疑问,这几个save执行的sql都在一个数据库事务里的吗?没看见有显式声明,不清楚有没有
作者回复: 在 application service 层有 @Transactional 注释,所以是在同一个事务。难道我在代码中漏了?
2023-08-25归属地:广东1 - 苏籍老师好,有几个困惑,想请教一下 1. 关于聚合中有多个实体,比如Emp 中有 skill 和 经验, 在实际场景中,业务场景上只需要更新 skill,在操作数据库时候,有必要Emp也更新吗(我看示例代码上 写的 保存完Emp 再去保存skill),我只更新skill 是否可行呢 或者我能够在领域层提供一个修改skill的领域服务。 2. 我看前面UpdateEmp方法执行之前,进行变更Emp和skill 属性的操作的 EmpUpdator 是放在应用层的,我理解是不是应该放在领域层呢,首先因为实体属性的变更 本身应该是某个业务规则触发的,在某个业务规则下才能修改某些属性以及联动修改skill 这种应该属于领域逻辑吧。 另外聚合本身后续可能会拆解成微服务,如果这种写到应用层,不利于后续拆分
作者回复: 1 之所以总是更新Emp,是因为乐观锁(version字段)放在了 Emp 上。如果不想更新Emp,可以另外找一个地方放乐观锁。 2. 目前EmpUpdator用到了DTO,如果直接放到领域层,分层架构的依赖关系就错了,如果一定要放到领域层,那么可以在领域层再定义一层DTO。另外,EmpUpdator 并不是典型的领域逻辑,课程里应该说过,需要和领域专家聊的逻辑才是领域逻辑。
2023-07-03归属地:浙江1 - iam593继承于AuditableEntity的对象,在数据库中对应的表都有创建者、创建时间、修改者、修改时间等字段?从数据库层面看,这样会不会有点繁琐?
作者回复: 是的,都有这些字段。至于要不要这么做,取决于你的权衡。
2023-01-24归属地:湖南1 - 南山1.能直接从数据库中查询值构造聚合对象,不做任何检查或者校验可行吗? 2.查询emp就加写锁,语句使用forUpdate PS:这种方式的修改聚合很有启发性
作者回复: 第一点,关键是从数据库查到值以后,怎么构建领域对象。第二点,确实是悲观锁的可行做法。
2023-01-12归属地:江苏1 - Spoonselect for update不是一种很好的悲观锁方式,当A事务执行时,其他事务都在等待,占用数据库链接,数据库链接是一个很宝贵的资源,而且等待对于用户来说也是一种很不好的体验,还可能会有死锁的风险
作者回复: 有道理
2024-02-22归属地:浙江 - Johar1. 我们在重建聚合时,采用了编写聚合子类的方式绕过业务规则的校验,你还能想到其他方法吗? 直接在mybatis sql中将关联的实体查询出来,就不需要再单独实现了 2. 如果用悲观锁的话,应该怎样实现? 一般场景使用select *** for update,若是微服务要考虑使用分布式锁。 3.请教一下老师,目前在更新技能,工作经验,员工信息都在一起,要是更新场景频繁,是不是可以拆开单独更新,减小锁的范围?此外更新逻辑里面没有检验更新人的权限
作者回复: 3. 可以拆开。目前实际上共享了Emp中的一把锁(version字段),如果拆开,需要用多把锁。
2023-07-12归属地:重庆