DDD Repository 中的 save(T t) 方法实现 insert 和 update ,如果 t 中只有某个字段发生改变,那么只需更新这个字段即可,而不是更新全部字段,一般即使全部更新的话问题也不是很大,但是如果 t 中有列表或者设计到多张表,这个时候可能增加对 DB 的操作,请问如何解决这种问题?
从网上找到 snapshot 解决方法:
大家有没有什么实践或建议?
美团的分享:40:45分提了 Diff ,但是也不详细
1
huijiewei 2023-05-01 13:59:36 +08:00
这个是持久层的基础库应该考虑的事情,每个 ORM 都有动态更新查询的功能
|
2
TWorldIsNButThis 2023-05-01 14:17:27 +08:00
不清楚 hibernate 有没有相关配置
而且一般这种涉及多张表的也很少有高频次更新操作 |
3
vczyh OP @huijiewei 现在就是需要实现 Repository ,所以遇到这个问题,我感觉 ORM 解决不了这个问题,请大佬赐教,比如这样一个场景:
- save 一个 List<R> 一般是 rList.foreach(r=>save(r)),如果有的 r 没有修改,以上操作明显会导致多余的对 DB 的访问。 |
4
rozbo 2023-05-01 14:50:10 +08:00
这个问题可以通过和 ai 沟通得到它的看法,我觉得很有道理,ai 认为,性能不能仅从某一个点考虑,要考虑整体,比如是否符合逻辑,是否有容错性和副作用等,如果一味的追求极致的性能,应该直接操作 sql 语句。。。选择了 orm 就要接受它的低效率
|
5
huijiewei 2023-05-01 14:54:15 +08:00
|
6
vczyh OP @huijiewei 可能我没有表达清楚。我想表达的是 DDD 对持久化的影响,不是 ORM 批量的问题,DDD 聚合根中的一个 List 属性中的一个元素发生了变化,我实现的时候只想执行一条 update(item),而不是不管元素有没有修改,全部元素都执行一次 update ,即使一些 ORM 会对后者进行优化,我觉得不应该依赖这种,而且还得设置 allowMultiQueries=true 参数。
如果不用 DDD ,其实没有这种无效访问 DB 的问题。 |
8
huijiewei 2023-05-01 15:15:03 +08:00
@vczyh 如果聚合根里面的一个 List 属性中一个元素单独变化对聚合根没有影响的话,单独用领域对象去更新就好了。
https://insights.thoughtworks.cn/ddd-persist-aggregation/ DDD 这个说实话,没有领域专家介入,光靠程序员非常难。 |
9
rozbo 2023-05-01 15:20:25 +08:00
@vczyh 个人看法:DDD 就是个理想。。
DDD 还要求不让用导航属性呢,完全按照到 DDD ,可能确实提高了维护性,但是极大的降低了开发效率。 我觉得只要领悟它的思想就成,不必要 100%做到,做到“心中有剑”这一步就够了。 |
10
vczyh OP @rozbo 我现在越来越觉得是你说的这样的,太难实现了,但是他指导思想确实好,请问有推荐的架构没,可以实践的?
|
11
vczyh OP @huijiewei 博文中一个观点挺好的:让持久化入侵到领域服务,这样没有性能问题,整个领域内聚且逻辑可复用,只不过损失了领域不强依赖持久的优点。
|
12
Leviathann 2023-05-01 15:41:35 +08:00
确认了一下,hibernate 就是这样的,在事务结束时做 dirty check ,只有变更过的 entity 才会生成对应的 update statement
|
13
rozbo 2023-05-01 15:48:06 +08:00
@vczyh 我不知道你什么语言,dotnet 下有个框架叫 `abp`是我见过的对 ddd 规范理解最深刻的框架了。它实现了模块化,按照它的架构,项目中的每一个部分都是可以复用的,项目中的每个部分也都是可以替换的,比如可以把数据库从 pgsql 换成 mysql 甚至是 mongodb ,也可以无缝把基于依赖的实现换成基于微服务的实现(这一点我在别的框架完全没见过,可以说是 micro service ready ,原理是抽象了一个 application 层,application 层实现了这个模块的功能接口,同时自动生成了 application client 也实现了这个接口,如果有一天你想换成微服务,只要把 appcation interface 的实现换成 application client ,然后把 application 部署成一个独立的 endpoint 就可以无缝切换,代码都不用改)。
但是,你一眼都可以看到,为了实现这些美好的特性,它的开发效率是极低的,新建一个项目都有七八个细分项目,包括 domain 、domain shared 、application 、application contracts 、application client 、http api 、http host 等等,可以说非常的繁琐。但好在,如果你坚持严格按照它的 DDD 模式,你将有很多这些模块可以复用。。不过这一过程非常痛苦 |
14
crysislinux 2023-05-01 15:58:14 +08:00 via Android
我建议做好依赖反转就好了。domain 是最高级的,其他部分都依赖它。其他就不要想太多了。比如数据库我觉得没啥抽象的必要性,只要保证 repository 一级是抽象的就行了
|
16
vczyh OP @crysislinux 我现在想的是把 Repository 改一下,原来希望一个 save 方法把整个聚合根持久化,我现在可以增加多个方法,比如 saveOrderItem(OrderItem)
|
17
THESDZ 2023-05-01 16:06:14 +08:00
版本?或者显示的状态标记?
|
19
huwt 2023-05-01 20:29:10 +08:00
@vczyh 我用 fastapi 就是这样做的, 把持久化层的代码引入到 domain 里. 但是会带来一个问题, 难以约束数据类型, 不好做静态检查
|
20
huwt 2023-05-01 20:33:37 +08:00
|
21
echoless 2023-05-01 21:11:41 +08:00
@Leviathann #12 我观察过 python 的 sqlalchemy 也是类似的机制, 生成的 sql 是很精简的.
|
23
vczyh OP @huwt 就算让他进来也得统一通过 Repository ,不能把 dao 和 数据库实体入侵进来
|
25
echoless 2023-05-02 18:40:26 +08:00 via Android
@vczyh 这个从 object 到 db 的操作 如果你用了 orm 就是 orm 的事情 你不用管细节
|
26
nielinjie 2023-05-03 11:53:47 +08:00
实践中很难到关注到这个的程度。换句话说,此种调优几乎不会有必要。
如果有要做,需要考虑一致性的问题。 在多读多写的情况下,diff 后再写跟整个对象覆盖写的语意不同。需要根据业务要求进行区别。 |
27
857681664 2023-05-03 13:19:03 +08:00
看起来比较容易实现的是把 repo 层上升到 domain 层,然后自己做 diff ,再 update
|
28
kkbblzq 2023-05-03 22:30:19 +08:00
感觉用 DDD 主要还是借用思想,具体还是要根据当前公司 /项目的情况来进行适配的,并不是说存储层接口定义只能这样设计。。我上家公司推 DDD 一年之后,基本项目上都做了简化的,完全按 DDD 那套来要增加不少开发成本。
|