【问题标题】:Where do you put Validation in projects with Domain Driven Design?您在使用领域驱动设计的项目中将验证放在哪里?
【发布时间】:2010-12-15 03:30:04
【问题描述】:

我应该将域对象的验证逻辑放在我的解决方案中的什么位置?我应该把它们放在领域类、业务层还是其他地方?

我还想为此使用 Microsoft Enterprise Library 中的验证应用程序块和策略注入应用程序块。

我应该使用什么验证策略来很好地将所有这些组合在一起?

提前谢谢大家!

【问题讨论】:

    标签: c# validation domain-driven-design


    【解决方案1】:

    这取决于。首先 - 您需要了解您正在验证的内容。

    你可以验证:

    1. 您从 Http 帖子中检索到的值可以解析为日期时间,
    2. Customer.Name 不超过 100 个符号,
    3. 客户有足够的钱购买东西。

    如您所见 - 这些验证在本质上是不同的,因此它们是 should be separated。它们的重要性varies too(请参阅“所有规则都不是平等的”段落)。


    您可能要考虑的是not allowing 域对象处于无效状态。

    这将大大降低复杂性,因为在当前时间范围内,您知道该对象是有效的,并且您只需要验证当前任务相关的事情才能推进。

    此外,您应该考虑避免在您的域模型中使用工具,因为它应该尽可能地不依赖基础架构。

    另一件事 - 拥抱价值对象的使用。这些非常适合验证封装。

    【讨论】:

    • 嗨,阿尼斯。关于验证,您能否详细说明“拥抱值对象的使用”?什么意思?
    • @MattKocaj 值对象没有身份意味着它们没有生命周期意味着您需要在构造时验证它们一次
    【解决方案2】:

    首先,我同意@i8abug。

    但我确实想进一步谈谈建筑。这些设计架构中的每一个,比如领域驱动,都应该被视为一个建议,并应仔细审查。

    在每个步骤中,您都应该问自己,所讨论的问题对您的申请有什么好处和坏处。

    其中很多涉及添加大量代码并使项目严重复杂化,但收益甚微。

    验证点就是一个很好的例子。正如 Stefan 所说,单一职责原则基本上是说您需要创建一整套其他类,其目的是仅验证原始对象的状态。显然,这会为应用程序添加大量代码。也许它是为你生成的,也许你必须手写它。无论如何,更多的代码通常等同于不那么健壮,当然也等同于更难理解。

    分离所有这些的好处是您可以交换验证规则。好的。缺点是您现在需要为每个类定义查看和创建 2 个文件。即:更多的工作。您的应用是否需要更换验证规则?可能不是。我什至敢打赌说很少有人这样做。

    坦率地说,如果你沿着这条路走,那么你不妨将所有东西都定义为一个结构体,然后让所有这些“帮助”类慢慢回来,以完成验证、持久性、设置属性等的工作课程几乎没有给你带来任何东西。

    综上所述,我倾向于自包含课程。换句话说,他们知道他们的属性是如何相互关联的,并且知道什么是可接受的值。他们还可以对自己和他们的孩子进行手术。换句话说,他们知道自己是什么。这往往会导致简化的编码和实现。它还导致确切地知道去哪里进行修改或更改。我在这里真正做的唯一分离是实现控制反转以实现持久性;这允许我在运行时更换数据提供者;这是我完成的几个应用程序的要求。

    重点是,仔细考虑您正在做的事情,并确定这是否真的是您特定情况下的最佳选择。所有这些编程“规则”毕竟只是建议。

    【讨论】:

      【解决方案3】:

      你可以根据你的需要做任何一个。

      将它放在域类中可确保始终完成验证,但会使类变得臃肿。它也可能违反单一责任原则,具体取决于您如何解释(它增加了验证的责任)。将它放在域类中也会限制您进行一种验证。此外,除非您使用继承,否则可能必须在相关类 (DRY) 中多次实现相同的规则。如果您这样做,验证将在您的域中传播。

      外部验证(您可以通过 DI、工厂、业务层或上下文获取验证对象)确保您可以根据上下文交换验证规则(例如,对于您想要保存在部分完成的长时间运行的流程声明你可以有一个验证对象只是为了能够保存,另一个来检查域类是否真的有效并准备好使用)。您的域类将更简单(责任更少,尽管您必须进行最少的检查,如空检查,以防止运行时错误),并且您也可以为相关类重用规则集。以这种方式,验证集中在域模型的一小块区域中。顺便提一句。您可以将外部验证注入域类本身,确保这些类确实验证自己,只是不知道它们在验证什么。

      虽然无法对验证应用程序块发表评论。与往常一样,您必须权衡利弊,从来没有一个有效的解决方案。

      【讨论】:

      • +1 as Martin Folwer mentions,我们可以认为验证是我们需要根据上下文运行的东西。如果我们想将其保留在实体的类中,可以采用validateForBlahBlah 等方法的形式。
      【解决方案4】:

      我一般把它放在域对象中。这是因为域对象是我关心的验证内容,因此如果特定对象的规则发生更改,我知道在哪里更新它,而不必在某些特定验证类/文件中搜索一堆不相关的实体规则.

      我意识到这可能不被视为 POCO,但每个项目都有特定的例外,这对我来说通常很有意义。同样,在某些项目中,从视图中引用您的域实体是有意义的,因此,实现 IPropertyChanged 而不是不断地将值从实体复制到另一组视图特定对象。

      我进行验证的旧方法是我有一个如下所示的 IValidator 接口,每个实体都实现了该接口。

      public interface IValidator
      {
         IList<RuleViolation> GetViolations();
      }
      

      现在我使用 NHibernate Validation 执行此操作(不需要使用 nhibernate ORM 来利用验证库。它只是通过属性完成。

      //I can't remember the exact syntax but it is very similar to this
      public class MyEntity
      {
      
      [NHibernateValidation(Length(min=1, max=10)]
      public String Name {get;set;}
      
      }
      
      //... and then later ...
      NHibernateValidator.Validate(myEntity);
      

      编辑:由于 Chris 告诉我它现在与 NHibernate Validation 非常相似,因此我删除了关于过去不是企业库的忠实粉丝的评论

      【讨论】:

      • +1:我做几乎完全相同的事情。唯一的区别是我使用企业库中的验证器。
      • 啊酷。我刚刚查找了企业库的验证应用程序块。看起来它比我记得的更类似于 nhibernate 验证器。
      猜你喜欢
      • 2021-01-21
      • 1970-01-01
      • 2016-07-31
      • 1970-01-01
      • 1970-01-01
      • 2010-10-05
      • 2017-01-12
      • 2017-11-13
      相关资源
      最近更新 更多