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

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

  •  
  •   vczyh · 333 天前 · 2703 次点击
    这是一个创建于 333 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    从网上找到 snapshot 解决方法:

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

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

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

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

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

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

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