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

我理解的 Smart Domain 与 DDD

  •  
  •   BrightLiao ·
    gmlove · 2022-08-09 09:25:45 +08:00 · 2189 次点击
    这是一个创建于 838 天前的主题,其中的信息可能已经有所发展或是发生改变。

    前段时间,Thoughtworks CTO 八叉在极客时间做了一次关于用 Smart Domain 实现 DDD 的分享(点击这里回看)。一个新词 Smart Domain 进入大家都视野。

    Smart Domain 是啥?为什么可以用 Smart Domain 实现 DDD ?

    八叉在分享中提到 Smart Domain 这个名字来源于 Smart UI 。我们都知道 Smart UI 是 DDD 中提到的一种反模式,只能用于解决简单问题。这里的命名略带反讽戏谑的意味。

    结合以往对 DDD 的学习和实践的经验,跟大家分享一下个人的理解: https://brightliao.com/2022/07/27/smart-domain-and-ddd/

    欢迎大家批评指正。

    13 条回复    2022-08-10 20:41:30 +08:00
    jamel
        1
    jamel  
       2022-08-09 09:58:08 +08:00
    我是 ddd 新手。以下是我的个人见解:
    对于 Smart Domain 术语,我 google 了相关内容,没有找到具体的详细信息,这个术语是否是 Thoughtworks 自己提出来的?是否有相关的文献 解释这个术语。
    关于分层这块有很多架构:六边形架构、洋葱架构、整洁架构、菱形架构、事件驱动架构、CQRS 等
    yule111222
        2
    yule111222  
       2022-08-09 10:00:12 +08:00
    很不错的尝试值得参考,未来考虑在一些新项目里小范围尝试
    目前还是主要用 菱形对称架构 除了有点重,其他都感觉很好
    BrightLiao
        3
    BrightLiao  
    OP
       2022-08-09 15:17:42 +08:00
    @jamel
    Smart Domain 是作者对这一 DDD 实现方式的命名。是一个新词。第一句话里面有提到。

    至于文献,目前应该是没有的。其实工程实践类的专有文献一般比较少,毕竟不是科研。比如 DDD ,可能也找不到啥专门解释它的论文。有关工程实践类的书倒是不少,也许八叉后续会写本书来介绍 Smart Domain 吧。
    BrightLiao
        4
    BrightLiao  
    OP
       2022-08-09 15:33:57 +08:00
    @jamel
    分层架构的问题是,容易让开发人员把注意力放在分层上(导致领域模型抽象不足),而忽略了领域层建模才是 DDD 的核心。Smart Domain 可以把大家的注意力转移到领域层建模上。
    就像 Eric 说的 DDD“远远不只是 ‘发现名词’,业务活动和规则如同所涉及的实体一样,都是领域的中心… 当我们的建模不再局限于寻找实体和值对象时我们才能充分吸取知识… ”。

    其实,在分层架构下,对于合理的分层,现在的框架都有很好的支持,基本上都是利用框架的能力实现了。这也是应该弱化分层的理由之一。
    BrightLiao
        5
    BrightLiao  
    OP
       2022-08-09 15:34:22 +08:00
    @yule111222 欢迎回来分享使用经验啊!
    yule111222
        6
    yule111222  
       2022-08-09 16:57:15 +08:00
    @BrightLiao 《解构领域驱动设计》里面有详细介绍菱形对称架构,我觉得很好,非常符合 整洁架构的思想,也就是外层技术机制,应用业务规则都去依赖 核心领域实体。 核心的 domian 层没有任何外部依赖,这一点 Smart Domain 也做到。不过 Service,Repository 姑且不论。我坚持认为 Aggregate 是必须的,这是让领域对象可测试可管理可解耦的关键。就拿测试来说,如果针对每个领域对象一对一的写测试,那么测试代码就会跟被测试的代码高度耦合,模型层的任何调整都可能引起大量测试失败。只针对聚合根的行为去编写测试就可以对聚合根之外的对象有效解耦
    BrightLiao
        7
    BrightLiao  
    OP
       2022-08-10 09:16:53 +08:00
    @yule111222
    聚合的问题在于粒度难以把握。现在大家都倾向于用小聚合,那么多小合适呢?之前的小聚合,是不是可能由于聚合内的实体逐渐变复杂而需要拆分呢?
    测试更加不是一定要有聚合的理由。如果逻辑拆分合理,每一个实体应该都有自己的业务能力。测试应该针对每个实体的公开 API 来测试。如果直接对聚合上面的 API 来测试,则会把分散到各个聚合内的实体的业务逻辑一并测试到,从而导致需要覆盖的场景特别多特别复杂。

    以上是个人的理解。
    yule111222
        8
    yule111222  
       2022-08-10 09:28:00 +08:00
    @BrightLiao 如何划分聚合有一整套原则和操作指南,《解构领域驱动设计》里面有详细介绍。
    总的来说就是先判断关系,泛化和物理包容关系的先无脑划分到一个聚合里面,其他的关系一律隔离开,然后再做微调去拆分过大的聚合。如何判断是否过大有经验和子实体是否有一定的独立生命周期来判断
    通过聚合来测试我个人实践下来是非常好用的,没有遇到什么麻烦,可能我的聚合力度不是太大也有关系吧。话说回来,微服务架构其实单测我写的不多,验收测试写的更多,少数核心业务逻辑可能会用 UTDD 来做和验收测试比较难覆盖的分支才会用单测弥补。
    可以参考这个 https://insights.thoughtworks.cn/test-pyramid/
    BrightLiao
        9
    BrightLiao  
    OP
       2022-08-10 09:50:05 +08:00
    @yule111222 我一般的做法是用 TDD 来驱动复杂场景的模型设计,从而尽可能丰富领域模型,同时还顺便把测试加上了。所以,我会觉得用 TDD 可以驱动实现 DDD 。(简单场景的话,凭经验进行设计就搞定了)

    打一波广告,最近刚开源了一个用于进行 ETL 开发的工具和库: https://github.com/easysql/easy_sql
    有兴趣的同学可以研究一下里面的领域模型设计及测试(目前有 80%+的测试覆盖率)。
    yule111222
        10
    yule111222  
       2022-08-10 14:47:50 +08:00
    @BrightLiao TDD 可以实现所有程序,前提是 use case 很明确了。但是在概念建模和领域建模的时候,往往用例也还不是那么的明确,很多东西需要跟领域专家一起从模型层面推导出来。聚合还有一点就是可以约束领域对象之间和控制器与领域对象之间的依赖关系。防止形成过于复杂的对象网
    BrightLiao
        11
    BrightLiao  
    OP
       2022-08-10 15:48:33 +08:00
    @yule111222

    DDD 的要旨之一是建模人员是 Hands-on modeler 。所以,虽然可以有概念建模和领域建模的阶段,但是这只是建模过程的初始一小段,建模是要在整个软件开发过程中持续进行的(建模的结果绝不仅仅是发现了几个名词,还包括要深刻的建模业务规则,通过引入新的概念来抽象和简化规则)。事实上,如果初始的各种图没有持续更新,很快就会变成没用的或者误导团队的废纸。

    如何做好建模呢? DDD 原书提到可以:

    - 大声的建模(第 2.2 节):充分利用人类的语言天赋,大声的讨论、交流,最终得到一个统一的大家都理解的语言。“这个过程可以帮我们精练模型”。这是可以用 TDD 来驱动实现 DDD 的支持之一,因为在还没有代码的时候写测试,我们只能利用自己的语言能力去描述解决问题的过程。
    - 绑定模型和实现(第 3 章): 在进行 DDD 时,不能将建模过程和实现过程分离,因为实现的一点点改变就意味着模型的改变。如果有人只关注建模而不关注实现,则他的建模没有用处,实际的模型将牢牢掌握在实现这个模型的开发人员手中。所以 DDD 的建模人员需要是 Hands-on modeler 。TDD 其实就是 Hands-on modeler 的武器。
    - 通过重构加深理解(第三部分):在已有代码的基础上,从多个方向进行重构,尝试不同的建模想法,创造突破,从而得到更深刻的模型。TDD 产生的测试在重构的过程中为我们保驾护航。

    TDD 的价值不只是在于测试覆盖,这只是它的一个副产物而已。TDD 的更大的价值在于可以用于改善设计(也即驱动 DDD ),我的另外几篇文章里面有一些思考:

    - 从改善设计的角度理解 TDD: https://brightliao.com/2019/07/20/tdd-for-improving-design/
    - 从改善设计的角度理解 TDD ( 2 ): https://brightliao.com/2019/08/18/tdd-for-improving-design-2/
    - 好代码的五个特质 - CUPID (基于领域的章节): https://brightliao.com/2022/05/24/5-properties-of-good-code-cupid/

    初始阶段进行一定程度的概念建模和领域建模是有用的,但即便在这个过程,TDD 也可以帮助我们提取和改善领域模型,除非我们的目标只是产出一个不知道是不是可以实现的设计文档(而不是一个可运行的 MVP )。从整个开发过程来看,在比初始阶段长的多得多的开发时间里进行建模,TDD 是有很大作用的。
    yule111222
        12
    yule111222  
       2022-08-10 17:23:57 +08:00
    @BrightLiao 嗯,是这个道理,我也很热衷用 UTDD 驱动代码和模型成型,因为我有测试覆盖强迫症,被 UTDD 驱动出来的代码天然就双 100%覆盖了,不用后面再去补。但是这个跟聚合不矛盾,没有聚合的情况下,面对成群的领域对象,如何管理对象之间的依赖关系呢,稍不注意就可能形成复杂的对象网?
    BrightLiao
        13
    BrightLiao  
    OP
       2022-08-10 20:41:30 +08:00 via Android
    @yule111222

    聚合我也认为没什么不好(前提是设计合理),我也经常用,只不过在 smart domain 中聚合被弱化了,运行时就是一个复杂的对象图(在一个独立的内聚的领域里看,这本来就是事实)。如果要从 smart domain 对象图里面寻找聚合,一般而言,就是通过内存模型关联的一组对象。但是 smart domain 的灵活性在于可以透明的将内存关联变为数据库关联,甚至跨服务关联。

    不过将 smart domain 和直接引用的聚合结合起来用,也是值得尝试的。事实上,我在文章中也提到可以尝试这样做。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1244 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 103ms · UTC 23:49 · PVG 07:49 · LAX 15:49 · JFK 18:49
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.