【问题标题】:How to create rich domain objects while maintaing persistence ignorance?如何在保持持久性无知的同时创建丰富的领域对象?
【发布时间】:2011-06-02 14:56:43
【问题描述】:

首先,我使用的是没有任何 ORM 框架的 Web 表单。

我一直在努力使我的域对象尽可能“智能”和“丰富”,而不允许它们访问我的服务和存储库层。我最近的尝试是为在线商店创建礼券模型。

我看到的主要反复出现的问题是:

  • 越来越多的逻辑不断被引入服务层。所有对存储库的调用都必须通过服务层,并且每次验证参数时(例如 - 存在于数据库中等)。结果,我的服务层正在增长,但我的域对象只有一些简单的合同验证。甚至对象验证也在服务层,因为如果项目的 ID 为空,它会检查 db 以确保代码是唯一的。 IHMO,系统的消费者不应该关心他们需要的功能是否处理持久性。

  • 我有一个单独的 POCO 用于兑换礼券时的交易日志条目。我假设我应该将这些交易的列表或集合作为我的礼券模型的属性,但我仍然不确定何时应该填写该属性。我是否在服务上添加了一个单独的方法来按需将交易加载到对象中(例如 - LoadTransactions(gc object)),或者是否应该在请求现有礼券或礼券列表时自动加载交易(或者可能getGC 中的一个选项也可以加载事务)

  • 诸如“可用余额”之类的计算字段呢...我什至应该在我的对象上拥有这样的属性吗?每当我使用该对象时,我都需要不断更新该属性以确保它是最新的。现在我只有一个服务方法 GetBalanceByCode(gc code)。

  • 甚至像兑换礼券这样的操作基本上都是 100% 以数据为中心的(获取一些输入参数,验证它们并将交易日志条目添加到 db)。

【问题讨论】:

    标签: c# asp.net architecture domain-driven-design repository-pattern


    【解决方案1】:

    越来越多的逻辑不断出现 在服务层中引入(...) 甚至对象验证也在 服务层(...)

    验证不是作为域模型元素的最佳候选者。输入(我个人的偏好是它表示为命令)应该在应用程序服务级别进行验证。领域逻辑应该对业务如何工作进行建模,并假设所有参数都是有效的。领域逻辑的良好候选者是计算,例如:您希望将它们放在一个地方并对其进行良好测试。

    我有一个单独的 POCO 用于交易 礼物时的日志条目 证书已兑换。

    这种对象称为事件。您可以从 Eric Evans 'What I learnt since the Blue Book' 演示文稿中了解事件。事件基本上是一个不可变的实体。事件通常是它们自己的聚合,因为它们通常有很多。通过使它们聚合,您不会遇到任何延迟加载它们作为其他对象集合的问题的问题。

    计算字段怎么样 “可用余额”......我应该什至 在我的身上有这样的属性 对象?

    计算属性是一种自然适合领域模型的逻辑,但是,是否有更好的方法是每次计算值或在对象更改时计算值并将其保存在数据库中,这是值得商榷的。

    甚至像兑换礼物这样的行为 证书基本上是100% 以数据为中心(接受一些输入 参数,验证它们并添加一个 db 的事务日志条目)。

    此操作将被建模为创建 CertificateRedeemed 事件。此事件可能由证书聚合或其他对象创建。 Udi Dahan 的 blog post 可能会有所帮助

    【讨论】:

      【解决方案2】:

      这不是一个完全容易回答的问题,因为域模型非常主观,并且很大程度上依赖于您的……嗯,域。听起来您实际上正在创建类似于 Jeffery Palermo 描述的The Onion Architecture(和Part 2)的东西。这不是一个不好的模式,尽管 DDD 纯粹主义者会告诉您它会导致“贫血”域模型(您的域对象基本上是没有行为的数据持有者)。问题是,这可能正是您在场景中所需要的。一个“完整、丰富”的域模型可能对你正在做的事情来说太过分了(鉴于你的最后一个要点,听起来可能就是这种情况)。

      您的系统可能根本不需要域模型。您可以使用一些视图模型(即描述您的视图的简单数据模型)并让您的 UI 通过您的服务发送一些 DTO 以将数据放入数据库中。如果您发现需要更复杂的方法的东西,那么您可以将更丰富的领域模型应用于该组件。另请记住,您的系统中不一定有 一个 域模型。可以并且在许多情况下应该有不同的模型来以不同的方式描述事物(通常分组为Bounded Contexts)。 DDD 的总体目标是简化其他复杂的系统。如果它给你带来了额外的复杂性,那么你可能会走很长的路。

      【讨论】:

      • 感谢您的评论。我是否错误地认为我的 GiftCertificate POCO 不应该能够调用我的 GiftCertifcateService 或 Repo 上的方法?
      • 如果您的系统看起来像巴勒莫的洋葱架构,那么这是完全正确的。这往往会将大量逻辑推入服务中,因此在某种程度上,您的服务会为您的域行为建模,然后您的 POCO 基本上就是服务操作的数据位。
      • 我正在尝试更多地转向 DDD 和存储库模式。我的假设是基于 POCO 的持久性无知规则。如果我的 POCOS 可以与服务层对话,那么我当然会将逻辑移至域对象。
      • 通常您的 POCO 不会引用服务,但您的服务依赖于您的 POCO。同样,您的服务将使用存储库来执行适当的操作。因此,GiftCertificateService 将有一个 Redeem 方法,该方法将采用您的 GiftCertificate POCO,并使用 GiftCertificateRepository 适当地保存数据。由于我们谈论的是 DDD,我觉得有义务提及您的存储库应该只存在于您的聚合根,这意味着您的存储库将持久化聚合以及作为聚合一部分的所有实体。
      【解决方案3】:

      有一种称为DCI (data-context-interactions) 的方法应该可以替代旧式OOP。尽管它没有明确解决持久性无知的问题,但您的问题让我想起了它,因为它涉及类似的问题。

      在 DCI 域中,​​对象是只有一点逻辑的小型数据持有者,就像您的情况一样,它们之间的交互是单独实现的。交互的算法不是通过几个对象的小方法来传播的,而是在一个地方,这样可能会更清晰易懂。

      我认为这仍然是一个学术问题,而不是我们明天应该开始实施的解决方案,但遇到这个问题的人可能会感兴趣。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多