首先,我使用没有任何ORM框架的Web表单。
我一直在努力使我的域对象成为“智能”和“丰富”,因为它们可以在不允许他们访问我的服务和存储库层的情况下。我最近的尝试是为在线商店创建礼品券模型。
我看到的主要反复出现的问题是:
越来越多的逻辑不断引入服务层。对存储库的所有调用必须通过服务层,并且每次验证参数时(例如 - 存在于db等中)。结果,我的服务层正在增长,但我的域对象只有一些简单的合同验证。甚至对象验证也在服务层中,因为如果项的ID为null,它将检查db以确保代码是唯一的。 IHMO,系统的消费者不应该关心他们需要的功能是否与持久性有关。
我有一个单独的POCO用于兑换礼券时的交易日志条目。我假设我应该将这些交易的清单或集合作为我的礼券模型的属性,但我仍然不确定何时应该填写该属性。我是否在服务上添加了一个单独的方法,用于将事务加载到按需对象(例如 - LoadTransactions(gc object)),或者是否应该在请求现有礼品证书或礼品证书列表时自动加载事务(或者getGCs中的一个选项,用于加载事务)
计算字段如“可用余额”怎么样?我的对象上是否还应该有这样的属性?无论何时我使用该对象,我都需要不断更新该属性以确保它是最新的。现在我只需要一个服务方法GetBalanceByCode(gc code)。
甚至像兑换礼品券这样的操作基本上都是100%以数据为中心的(获取一些输入参数,验证它们并向db添加事务日志条目)。
答案 0 :(得分:4)
越来越多的逻辑不断存在 介绍在服务层(...) 甚至对象验证也在 服务层(...)
验证不是域模型元素的最佳候选者。输入(我的个人偏好是它表示为命令)应该在应用程序服务级别进行验证。域逻辑应该模拟业务的工作方式并假设所有参数都是有效的。域逻辑的良好候选者是计算,例如:你想将它们放在一个地方并经过充分测试。
我有一个单独的POCO用于交易 记录礼物的日志条目 证书被赎回。
这种对象称为事件。您可以从Eric Evans 'What I learnt since the Blue Book'演示中了解事件。事件基本上是一个不可变的实体。事件通常是自己聚合的,因为通常有很多事件。通过使它们成为聚合,您可以将延迟加载它们作为其他对象集合的一部分。
计算字段怎么样? “可用的平衡”......我应该 在我的上面有这样的属性 对象
计算属性是一种自然适合域模型的逻辑,但是如果更好的方法是每次计算值或者在对象更改时计算它并将其保存在数据库中,那么这是有争议的。
甚至像赎回礼物这样的行为 证书基本上是100% 以数据为中心(采取一些投入 参数,验证它们并添加一个 事务日志条目到db)。
此操作将建模为创建CertificateRedeemed事件。此事件可能由证书聚合或某些其他对象创建。 Udi Dahan的blog post可以提供帮助
答案 1 :(得分:3)
这不是一个完全容易回答的问题,因为领域模型非常主观,并且很大程度上依赖于你的......好吧,领域。听起来你实际上正在创建类似于Jeffery Palermo所描述的The Onion Architecture(和Part 2)的东西。这不是一个糟糕的模式,虽然DDD纯粹主义者会告诉你它会导致“贫血”的域模型(你的域对象基本上是没有行为的数据持有者)。问题是,这可能正是您在场景中所需要的。一个“完整,丰富”的域模型对于你正在做的事情可能有点过分(并且考虑到你的最后一个要点,听起来可能就是这种情况)。
您可能根本不需要系统的域模型。您可以很好地使用一些View Models(这是描述您的视图的简单数据模型),并让您的UI通过您的服务发送一些DTO以将数据放入数据库。如果您发现需要更复杂方法的内容,则可以将更丰富的域模型应用于该组件。另请注意,您的系统中不一定有一个域模型。可以并且在许多情况下应该是以不同方式描述事物的不同模型(通常分组为Bounded Contexts)。 DDD的总体目标是简化其他复杂系统。如果它给你带来额外的复杂性,那么你可能会走很长的路。
答案 2 :(得分:0)
有一种名为DCI (data-context-interactions)的方法应该是旧学校OOP的替代方案。虽然它没有明确解决持久性无知的问题,但是你的问题在我脑海中浮现,因为它处理类似的问题。
在DCI域中,对象是小数据持有者,只有很少的逻辑,就像你的情况一样,它们之间的交互是分开实现的。交互算法不是通过几个对象的小方法传播,而是在一个地方,这可能使它更清晰和可理解。
我认为它仍然是学术性的东西,而不是明天我们应该开始实施的解决方案,但遇到这个问题的人可能会感兴趣。