数据层或域层的规则验证?

Posted

技术标签:

【中文标题】数据层或域层的规则验证?【英文标题】:Rules Validation on Data or Domain layer? 【发布时间】:2010-11-26 12:53:46 【问题描述】:

我一直在阅读 Pro ASP.NET MVC Framework,Steven Sanderson,在第 11 章中讨论了数据验证。

在第 390 页,我们看到了将验证逻辑移动到您的模型层部分。在本节中,我们在第 392 页看到了一些代码,展示了如何实现验证。

代码实现了GetRuleViolations() 方法,Save() 方法使用它在出现问题时抛出RuleException

然而,在我看来,领域层和数据访问层之间没有区别,代码如下:

public void Save() 
    var errors = GetRuleViolations();
    if (errors.Count > 0)
        throw new RuleException(errors);

    // Todo: Now actually save to the database or whatever

private NameValueCollection GetRuleViolations() 
    // validations...

在我正在工作的一个项目中,我有一个 Domain 层,尽可能地忽略持久性,还有一个 Data Access 层,通过 NHibernate 实现数据访问,并实现在域层中定义接口的存储库。

如果我按照作者在此处建议的方式实施验证规则,在“Save()”方法上,它们将继续我的数据访问层,尽管至少我认为它们应该驻留在域模型上!

所以,我的问题是:在创建分层应用程序时,域层实现域实体并将接口暴露给存储库( persistence ignorant),数据访问层从域层实现存储库并实现所有数据访问代码,验证规则应该驻留在哪里 ?

我的主要(或至少第一个)界面将是一个 ASP.NET MVC 应用程序,如果这可能会改变的话。

谢谢。

【问题讨论】:

实际上我有两个不同的想法在我的脑海中争斗:(1)如果我决定写一个新的 DAL,我不想重写所有相同的规则(DRY)——以避免错过一条规则,或产生错误,或必须多次重新实施所有新规则; (2) 很难在域上实现“上下文验证”(因为我每次处理它时都必须在域上注入有关上下文的详细信息)...... 【参考方案1】:

在 MVC 架构中,M(模型)包括both领域层数据访问层。所以桑德森的例子没有错。

也就是说,当您使用这两个层(而不是只有一个)实现域模型时,验证逻辑应该转到域层以增加域对象的凝聚力并避免验证逻辑在许多地方重复(例如在每个具体的存储库中)。

【讨论】:

我同意,但是“上下文验证”呢?例如,域对象是有效的,它们已经准备好被持久化,但是有一条规则说如果通过用户的窗口可以看到一辆红色的汽车,那么他就不能保存该对象。此规则携带有关 保存 操作的地点/时间的上下文信息。该信息将来自一项服务,该服务提供所有附近停放的汽车的列表(即,不在您的项目中的东西)。该验证规则将在哪里执行,同时遵循 DRY 原则? 在执行保存的服务方法中。我通常在我的 ASP.NET MVC 应用程序中有这些层:控制器 --> 服务 --> 存储库。跨这些层使用域对象。服务(不是控制器)应该在域对象中触发验证。然后由服务执行上下文验证,例如保存域对象。所有这些东西(例如服务、存储库和域对象,即实体)都是域驱动设计的概念。为什么干燥?例如。您可以在 WebForms 应用程序中重用所有这些(服务、存储库和实体)而无需更改。 Buu Nguyen:Martin Fowler 指出“你在服务中发现的行为越多,你就越有可能剥夺自己从领域模型中获得的好处。如果你所有的逻辑都在服务中,你把自己骗瞎了。” (martinfowler.com/bliki/AnemicDomainModel.html) 说,你的模型贫血。【参考方案2】:

它们绝对属于您的域层(您可以在其中实现 IDataErrorInfo,但我认为这只对 Windows 窗体或 WPF 应用程序有用)。

看起来这种验证理念与Paul Stovell 公开的理念非常相似(请查看this article of his)。它非常强大,我经常使用它。基本上:

    拥有无效的业务对象并没有错,只要您不尝试持久化它。 应该可以从业务对象中检索任何和所有损坏的规则,以便数据绑定以及您自己的代码可以查看是否存在错误并适当地处理它们。

因此,尽管您的 域层 对持久性问题一无所知,但我相信您的实体至少应该知道它们何时被持久化。 Save 方法是一种让它们负责自己的持久性的方法(它们随后可以委托给数据访问层)。我看不出这有什么问题。

【讨论】:

我不同意,正如 Jimmy Nilsson 在他的 DDD 书中所写的那样,“所有状态,即使出现错误,也应该是可挽救的”。他使用的示例是:“用户讨厌从应用程序中得知由于错误而无法保存当前更改。我的一个朋友将其比作如果您有拼写错误则不允许保存文字处理文档。” 【参考方案3】:

我通常更喜欢始终有效域对象。 域对象只能通过防止对象变为无效的方法来更改。

另一方面,表示对象可能包含临时无效值或无法正确解析的值。但是方法调用只有在数据有效时才会由表示层发出给领域对象。

域对象强制执行不变量,表示对象向用户指示它应该如何修改其输入以遵守约束。

【讨论】:

【参考方案4】:

验证应该在您的域层中完成。您的业​​务逻辑是域的一部分,而不是数据访问。验证可能不在域对象本身(实际类)内部完成,但它应该存在于域层内部。

【讨论】:

以上是关于数据层或域层的规则验证?的主要内容,如果未能解决你的问题,请参考以下文章

域层访问持久性的东西

DDD:Dto 汇编器是不是应该成为域层的一部分?

模型层的业务规则

ThinkPhp 3.2 自动验证

XAF实现运行时填加验证规则并保存到数据库中

在 DDD 中将存储库实现保存在哪里?