首页 > 解决方案 > 实体/聚合中的 DDD 可重用功能

问题描述

我在 DDD 中有以下设计

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

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

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

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

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

我现在选择了第一个选项,但我觉得不太舒服,我相信必须有更好的方法来做到这一点。

标签: entityaggregatedomain-driven-designaggregate-functions

解决方案


我现在选择了第一个选项,但我觉得不太舒服,我相信必须有更好的方法来做到这一点。

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

从某种意义上说,如果 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 实例寿命更长。

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


推荐阅读