DDD 持久化的时候如何避免无效 DB 操作?

364 天前
 vczyh

DDD Repository 中的 save(T t) 方法实现 insert 和 update ,如果 t 中只有某个字段发生改变,那么只需更新这个字段即可,而不是更新全部字段,一般即使全部更新的话问题也不是很大,但是如果 t 中有列表或者设计到多张表,这个时候可能增加对 DB 的操作,请问如何解决这种问题?

从网上找到 snapshot 解决方法:

大家有没有什么实践或建议?

2757 次点击
所在节点    程序员
30 条回复
huijiewei
364 天前
这个是持久层的基础库应该考虑的事情,每个 ORM 都有动态更新查询的功能
TWorldIsNButThis
364 天前
不清楚 hibernate 有没有相关配置
而且一般这种涉及多张表的也很少有高频次更新操作
vczyh
364 天前
@huijiewei 现在就是需要实现 Repository ,所以遇到这个问题,我感觉 ORM 解决不了这个问题,请大佬赐教,比如这样一个场景:

- save 一个 List<R>
一般是 rList.foreach(r=>save(r)),如果有的 r 没有修改,以上操作明显会导致多余的对 DB 的访问。
rozbo
364 天前
这个问题可以通过和 ai 沟通得到它的看法,我觉得很有道理,ai 认为,性能不能仅从某一个点考虑,要考虑整体,比如是否符合逻辑,是否有容错性和副作用等,如果一味的追求极致的性能,应该直接操作 sql 语句。。。选择了 orm 就要接受它的低效率
huijiewei
364 天前
@vczyh 批量可以自己优化,https://developer.aliyun.com/article/1157550#slide-45

大部分 ORM 都可以实现的。多看文档
vczyh
364 天前
@huijiewei 可能我没有表达清楚。我想表达的是 DDD 对持久化的影响,不是 ORM 批量的问题,DDD 聚合根中的一个 List 属性中的一个元素发生了变化,我实现的时候只想执行一条 update(item),而不是不管元素有没有修改,全部元素都执行一次 update ,即使一些 ORM 会对后者进行优化,我觉得不应该依赖这种,而且还得设置 allowMultiQueries=true 参数。

如果不用 DDD ,其实没有这种无效访问 DB 的问题。
vczyh
364 天前
@rozbo 现在不用 ORM 也会有问题,我这个问题是基于 DDD 的。
huijiewei
364 天前
@vczyh 如果聚合根里面的一个 List 属性中一个元素单独变化对聚合根没有影响的话,单独用领域对象去更新就好了。

https://insights.thoughtworks.cn/ddd-persist-aggregation/

DDD 这个说实话,没有领域专家介入,光靠程序员非常难。
rozbo
364 天前
@vczyh 个人看法:DDD 就是个理想。。
DDD 还要求不让用导航属性呢,完全按照到 DDD ,可能确实提高了维护性,但是极大的降低了开发效率。
我觉得只要领悟它的思想就成,不必要 100%做到,做到“心中有剑”这一步就够了。
vczyh
364 天前
@rozbo 我现在越来越觉得是你说的这样的,太难实现了,但是他指导思想确实好,请问有推荐的架构没,可以实践的?
vczyh
364 天前
@huijiewei 博文中一个观点挺好的:让持久化入侵到领域服务,这样没有性能问题,整个领域内聚且逻辑可复用,只不过损失了领域不强依赖持久的优点。
Leviathann
364 天前
确认了一下,hibernate 就是这样的,在事务结束时做 dirty check ,只有变更过的 entity 才会生成对应的 update statement
rozbo
364 天前
@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 模式,你将有很多这些模块可以复用。。不过这一过程非常痛苦
crysislinux
364 天前
我建议做好依赖反转就好了。domain 是最高级的,其他部分都依赖它。其他就不要想太多了。比如数据库我觉得没啥抽象的必要性,只要保证 repository 一级是抽象的就行了
vczyh
364 天前
@rozbo 用的是 Java ,不知道有什么成熟的模式可以妥协一下。
vczyh
364 天前
@crysislinux 我现在想的是把 Repository 改一下,原来希望一个 save 方法把整个聚合根持久化,我现在可以增加多个方法,比如 saveOrderItem(OrderItem)
THESDZ
364 天前
版本?或者显示的状态标记?
vczyh
364 天前
@THESDZ 这也就是我说的 Diff
huwt
363 天前
@vczyh 我用 fastapi 就是这样做的, 把持久化层的代码引入到 domain 里. 但是会带来一个问题, 难以约束数据类型, 不好做静态检查
huwt
363 天前
@vczyh 关于 ORM 低效读写问题, 之前看过有文章说, DDD 是面向变化, 那么偶尔的低效也无所谓, 重要的是维护状态一致.
这个跟 @rozbo 的说法是一致的.

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/936712

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX