【问题标题】:DDD reusable functionality in an Entity/Aggregate实体/聚合中的 DDD 可重用功能
【发布时间】:2020-08-09 19:39:42
【问题描述】:

我在 DDD 中有以下设计

  • 发布

    聚合
    • 正文:帖子的 HTML
  • 横幅实体与

    • Html:横幅的 HTML

Banner 实体属于 Post 聚合,所以我想在 Post 聚合中创建一个方法 BodyWithBanner

此方法的重点是搜索 Post.Body 的 HTML 并插入 Banner 的 HTML。

到目前为止,一切都很好。

但是我打算在抽象中重用这个功能:“在另一个 HTML 中插入一些 HTML”。所以我为此创建了一个不同的类:BannerReplacer

问题来了,我应该如何调用这个新类?

  • 只需在 Post.BodyWithBanners 方法中创建一个实例(打破依赖注入)
  • Post 聚合的构造函数中传递 BannerReplacer(这对于创建 Post 实例来说可能是一场噩梦)
  • BannerReplacer 传递给 BodyWithBanners 方法(这意味着使用 Post 的客户端必须处理 BannerReplacer

我现在选择了第一个选项,但我觉得不太舒服,我相信一定有更好的方法。

【问题讨论】:

    标签: entity aggregate domain-driven-design aggregate-functions


    【解决方案1】:

    我现在选择了第一个选项,但我觉得不太舒服,我相信一定有更好的方法。

    很多时候,第一个选项很好——所以你应该练习适应它。这主要意味着更多地考虑依赖注入的用途,并清楚地了解这些力量是否在这里发挥作用。

    如果 Banner 是一个实体,在domain-driven-design 意义上,那么它可能类似于内存状态机。它有一个它管理的数据结构,以及一些用于更改该数据结构或回答有关该数据结构的有趣问题的功能,但它没有 I/O、数据库、网络等问题。

    这反过来表明您可以在所有上下文中以相同的方式运行它 - 您不需要一堆替代实现来使其可测试。您只需实例化一个并调用它的方法。

    如果它在所有上下文中都以相同的方式运行,那么它不需要可配置的行为。如果你不需要能够配置行为,那么你就不需要依赖注入(因为这个实体的所有副本都将使用(副本)相同的依赖项。

    当您确实有可配置的行为时,分析将需要查看范围。如果您需要能够将该行为从一次调用更改为下一次调用,那么调用者将需要了解它。如果行为变化的频率低于此值,那么您可以开始研究“构造函数注入”是否有意义。

    您知道您打算对给定的方法调用使用单个 BannerReplacer,因此您可以立即从如下所示的方法开始:

    class Banner {
        void doTheThing(arg, bannerReplacer) {
            /* do the bannerReplacer thing */
        }
    }
    

    请注意,此签名完全不依赖bannerReplacer 的生命周期。更具体地说,BannerReplacer 的生命周期可能比 Banner 更长,或者更短。我们只关心生命周期是否比 doTheThing 方法长。

    class Banner {
        void doTheThing(arg) {
            this.doTheThing(arg, new BannerReplacer())
        }
    
        // ...
    }
    

    这里,调用者根本不需要知道 BannerReplacer;我们每次都会使用默认实现的新副本。调用者关心使用哪个实现可以自己传递。

    class Banner {
        bannerReplacer = new BannerReplacer()
    
        void doTheThing(arg) {
            this.doTheThing(arg, this.bannerReplacer)
        }
    
        // ...
    }
    

    和以前一样;我们只是使用了一个生命周期更长的 BannerReplacer 实例。

    class Banner {
        Banner() {
            this(new BannerReplacer())
        }
    
        Banner(bannerReplacer) {
            this.bannerReplacer = bannerReplacer;
        }
    
        void doTheThing(arg) {
            this.doTheThing(arg, this.bannerReplacer)
        }
    
        // ...
    }
    

    与以前的想法相同,但现在我们允许“注入”默认实现,该实现可以比给定的 Banner 实例寿命更长。

    从长远来看,舒适来自于进行分析以了解当前问题的需求,以便您可以选择合适的工具。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-06-29
      • 2016-11-05
      • 1970-01-01
      • 1970-01-01
      • 2021-10-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多