V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
vczyh
V2EX  ›  程序员

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

  •  
  •   vczyh · 2023-05-01 13:23:06 +08:00 · 3079 次点击
    这是一个创建于 557 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    从网上找到 snapshot 解决方法:

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

    第 1 条附言  ·  2023-05-01 14:48:52 +08:00
    30 条回复    2023-05-07 16:16:12 +08:00
    huijiewei
        1
    huijiewei  
       2023-05-01 13:59:36 +08:00
    这个是持久层的基础库应该考虑的事情,每个 ORM 都有动态更新查询的功能
    TWorldIsNButThis
        2
    TWorldIsNButThis  
       2023-05-01 14:17:27 +08:00
    不清楚 hibernate 有没有相关配置
    而且一般这种涉及多张表的也很少有高频次更新操作
    vczyh
        3
    vczyh  
    OP
       2023-05-01 14:44:42 +08:00
    @huijiewei 现在就是需要实现 Repository ,所以遇到这个问题,我感觉 ORM 解决不了这个问题,请大佬赐教,比如这样一个场景:

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

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

    如果不用 DDD ,其实没有这种无效访问 DB 的问题。
    vczyh
        7
    vczyh  
    OP
       2023-05-01 15:09:35 +08:00
    @rozbo 现在不用 ORM 也会有问题,我这个问题是基于 DDD 的。
    huijiewei
        8
    huijiewei  
       2023-05-01 15:15:03 +08:00
    @vczyh 如果聚合根里面的一个 List 属性中一个元素单独变化对聚合根没有影响的话,单独用领域对象去更新就好了。

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

    DDD 这个说实话,没有领域专家介入,光靠程序员非常难。
    rozbo
        9
    rozbo  
       2023-05-01 15:20:25 +08:00
    @vczyh 个人看法:DDD 就是个理想。。
    DDD 还要求不让用导航属性呢,完全按照到 DDD ,可能确实提高了维护性,但是极大的降低了开发效率。
    我觉得只要领悟它的思想就成,不必要 100%做到,做到“心中有剑”这一步就够了。
    vczyh
        10
    vczyh  
    OP
       2023-05-01 15:34:38 +08:00 via iPhone
    @rozbo 我现在越来越觉得是你说的这样的,太难实现了,但是他指导思想确实好,请问有推荐的架构没,可以实践的?
    vczyh
        11
    vczyh  
    OP
       2023-05-01 15:41:34 +08:00 via iPhone
    @huijiewei 博文中一个观点挺好的:让持久化入侵到领域服务,这样没有性能问题,整个领域内聚且逻辑可复用,只不过损失了领域不强依赖持久的优点。
    Leviathann
        12
    Leviathann  
       2023-05-01 15:41:35 +08:00
    确认了一下,hibernate 就是这样的,在事务结束时做 dirty check ,只有变更过的 entity 才会生成对应的 update statement
    rozbo
        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 模式,你将有很多这些模块可以复用。。不过这一过程非常痛苦
    crysislinux
        14
    crysislinux  
       2023-05-01 15:58:14 +08:00 via Android
    我建议做好依赖反转就好了。domain 是最高级的,其他部分都依赖它。其他就不要想太多了。比如数据库我觉得没啥抽象的必要性,只要保证 repository 一级是抽象的就行了
    vczyh
        15
    vczyh  
    OP
       2023-05-01 15:58:54 +08:00 via iPhone
    @rozbo 用的是 Java ,不知道有什么成熟的模式可以妥协一下。
    vczyh
        16
    vczyh  
    OP
       2023-05-01 16:03:55 +08:00 via iPhone
    @crysislinux 我现在想的是把 Repository 改一下,原来希望一个 save 方法把整个聚合根持久化,我现在可以增加多个方法,比如 saveOrderItem(OrderItem)
    THESDZ
        17
    THESDZ  
       2023-05-01 16:06:14 +08:00
    版本?或者显示的状态标记?
    vczyh
        18
    vczyh  
    OP
       2023-05-01 16:12:25 +08:00 via iPhone
    @THESDZ 这也就是我说的 Diff
    huwt
        19
    huwt  
       2023-05-01 20:29:10 +08:00
    @vczyh 我用 fastapi 就是这样做的, 把持久化层的代码引入到 domain 里. 但是会带来一个问题, 难以约束数据类型, 不好做静态检查
    huwt
        20
    huwt  
       2023-05-01 20:33:37 +08:00
    @vczyh 关于 ORM 低效读写问题, 之前看过有文章说, DDD 是面向变化, 那么偶尔的低效也无所谓, 重要的是维护状态一致.
    这个跟 @rozbo 的说法是一致的.
    echoless
        21
    echoless  
       2023-05-01 21:11:41 +08:00
    @Leviathann #12 我观察过 python 的 sqlalchemy 也是类似的机制, 生成的 sql 是很精简的.
    vczyh
        22
    vczyh  
    OP
       2023-05-02 18:13:13 +08:00 via iPhone
    @huwt 是的,有时候性能和可维护性要平衡一下
    vczyh
        23
    vczyh  
    OP
       2023-05-02 18:14:52 +08:00 via iPhone
    @huwt 就算让他进来也得统一通过 Repository ,不能把 dao 和 数据库实体入侵进来
    vczyh
        24
    vczyh  
    OP
       2023-05-02 18:16:31 +08:00 via iPhone
    @echoless Hibernate 没研究过,我的想法是更倾向于不依赖具体框架和语言
    echoless
        25
    echoless  
       2023-05-02 18:40:26 +08:00 via Android
    @vczyh 这个从 object 到 db 的操作 如果你用了 orm 就是 orm 的事情 你不用管细节
    nielinjie
        26
    nielinjie  
       2023-05-03 11:53:47 +08:00
    实践中很难到关注到这个的程度。换句话说,此种调优几乎不会有必要。
    如果有要做,需要考虑一致性的问题。
    在多读多写的情况下,diff 后再写跟整个对象覆盖写的语意不同。需要根据业务要求进行区别。
    857681664
        27
    857681664  
       2023-05-03 13:19:03 +08:00
    看起来比较容易实现的是把 repo 层上升到 domain 层,然后自己做 diff ,再 update
    kkbblzq
        28
    kkbblzq  
       2023-05-03 22:30:19 +08:00
    感觉用 DDD 主要还是借用思想,具体还是要根据当前公司 /项目的情况来进行适配的,并不是说存储层接口定义只能这样设计。。我上家公司推 DDD 一年之后,基本项目上都做了简化的,完全按 DDD 那套来要增加不少开发成本。
    vczyh
        29
    vczyh  
    OP
       2023-05-04 09:21:26 +08:00
    @kkbblzq 请教一下,请问做了哪些简化呢,想学习一下。
    vczyh
        30
    vczyh  
    OP
       2023-05-07 16:16:12 +08:00
    @nielinjie 这个角度很不错
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1215 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 18:13 · PVG 02:13 · LAX 10:13 · JFK 13:13
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.