DDD中领域层的DateTime.Now

3

最近我在域模型中遇到了以下不变量:

  1. 如果ExpiryAt(DateTimeOffset)< DateTimeOffset.Now,则将Offer视为已过期。

  2. 公司的董事不能年龄小于18岁。

  3. 下载文档时,我们应该使用DateTimeOffset.Now设置DownloadedAt字段。

在应用程序层中,为了保持纯洁性和更好的测试,我们通常使用IDateTime接口隔离System.DateTime,这样可以在单元测试中模拟Now。

但是所有这些3种情况都属于域层,而不是应用程序层。我们不应该将外部接口注入DomainModel以保持其纯洁性。但从另一方面来说,在DomainLayer中直接使用DateTime.Now或DateTimeOffset.Now可能是不好的,因为这会添加对系统时钟的依赖,并有时会使测试更加困难,因为DateTime.Now永远不会返回相同的结果。

那么问题是 - 如何解决这个困境?

我看到的选项:

  1. 将now作为参数提供给域实体方法。 这是可行的选择,虽然会使代码更冗长,有时甚至很愚蠢,但简化了测试。

  2. 在域层中直接使用DateTime.Now。我已经提到了这种方法的缺点。

从您的经验中,还有什么建议吗?

1个回答

3
从不同的选项中,访问静态的 DateTime.Now() 功能显然是最不利的。它既不允许测试,也隐藏了域模型对一些非确定性基础设施的依赖细节。
将某个 接口 注入到一个可以查看的服务中的选项要好一些,因为它使得 依赖明确,并且通过替换非确定性输出来返回您选择的一些 确定性 值,从而允许进行单元测试。
但是,在运行时,您的域模型需要访问一些基础设施依赖项。在某些情况下,这可能是一个合理的折衷方案,但如果可能,我会尝试避免这种情况,以保持域模型的纯洁性。
如果你从不同的角度来看待当前日期时间,它实际上就像一个普通的输入参数一样。您可以将其视为类似于当前日期时间的参考日期时间
对于您的第一个示例-检查报价是否过期-从域模型的角度来看,它需要在某个给定的时间点上检查报价是否过期。这个给定的时间点恰好是在域逻辑被执行的一个用例中,当前日期时间。
所以最终,我建议在这种情况下注入(当前)日期时间的值而不是接口。它使得需要哪些数据变得明确,以便域封装自己所需的数据并执行业务逻辑。
此外,它使客户端代码(例如用例或应用程序服务)想要告诉或询问域模型的内容更加明确。例如,如果需要,在现在检查报价是否已经过期,或者告诉我在某个重要的时间点报价是否已经过期,甚至是它们是否将在未来过期。
作为进一步阅读,我建议阅读Vladimir Khorikov的这篇出色的文章,他在这个主题上有更多的阐述。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接