如何在保持持久性无知的情况下创建丰富的域对象

本文关键字:创建 情况下 对象 持久性 | 更新日期: 2023-09-27 17:58:04

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

我一直在努力解决如何使我的域对象尽可能地"智能"answers"丰富",而不允许它们访问我的服务和存储库层。我最近的一次尝试是为网上商店创建一个礼券模型。

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

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

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

  • 像"可用余额"这样的计算字段呢。。。我的对象上应该有这样的属性吗?每当我处理对象时,我都需要不断更新该属性,以确保它是最新的。现在我只需要一个服务方法GetBalanceByCode(gc代码)。

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

如何在保持持久性无知的情况下创建丰富的域对象

越来越多的逻辑在服务层中引入(…)甚至对象验证也在服务层(…)

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

我有一个单独的POCO用于交易当收到礼物时记录条目证书已兑换。

这种对象被称为Event。您可以从Eric Evans的"我从蓝皮书中学到了什么"演讲中了解事件。事件基本上是一个不可变的实体。事件通常是单独聚合的,因为通常有很多事件。通过使它们聚合,将它们作为其他对象集合的一部分进行懒惰加载不会有任何问题。

像这样的计算字段呢"可用余额"。。。我应该扯平吗我的上有这样的属性对象

计算属性是一种自然适用于域模型的逻辑,然而,是否有更好的方法是每次计算值,或者在对象更改时计算值并将其保存在数据库中,这是有争议的。

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

此操作将被建模为创建CertificateRedemed事件。此事件可能是由证书聚合或其他对象创建的。Udi Dahan的这篇博客文章可能对有帮助

考虑到域模型非常主观,并且在很大程度上依赖于您的。。。嗯,域。听起来你实际上在创造一些类似于杰弗里·帕勒莫(Jeffery Palermo)所描述的洋葱建筑(以及第二部分)的东西。这是一个不错的模式,尽管DDD纯粹主义者会告诉你这会导致"贫血"的域模型(你的域对象基本上是没有行为的数据持有者)。问题是,这可能正是你在场景中所需要的。一个"完整、丰富"的领域模型可能对你正在做的事情来说有些过头了(考虑到你最后的要点,听起来可能就是这样)。

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

有一种称为DCI(数据上下文交互)的方法,它应该是老式OOP的替代方法。虽然它没有明确解决持续无知的问题,但你的问题让我想到了,因为它涉及类似的问题。

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

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