【问题标题】:How do I avoid duplicating validation logic between the domain and application layers?如何避免在域层和应用层之间重复验证逻辑?
【发布时间】:2013-02-04 22:33:12
【问题描述】:

我的领域模型中的任何给定实体都有几个需要强制执行的不变量——项目名称必须至少为 5 个字符,必须存在特定产品才能与项目相关联,截止日期不得早于当前日期和时间等。

显然我希望客户端能够显示与验证相关的错误消息,但我不想在程序的几个不同层之间不断维护验证规则——例如,在小部件、控制器中,应用程序服务或命令对象,以及域。另外,描述性错误消息似乎与表示相关,不属于域层。我该如何解决这些困境?

【问题讨论】:

标签: validation domain-driven-design dry code-duplication


【解决方案1】:

我会创建与您预期的错误条件相关的特定异常。这是一般异常处理的标准,有助于解决您的问题。例如:

public class ProjectNameNotLongEnoughException : System.Exception

public class DueDatePriorToCurrentDateException : System.Exception

在 xml cmets 中为可能引发异常的方法标记这些可能的异常,以便针对您的域模型编写的应用程序知道要注意这些异常,并能够在应用程序的表示中显示消息。这还允许您根据文化获得本地化的错误消息,而不会因表示问题而使您的域模型混乱。

如果您选择执行客户端验证,恐怕您也吃不到蛋糕。在这种情况下,您可能必须复制验证逻辑才能在维护架构的同时实现所需的功能。

希望这会有所帮助!

【讨论】:

  • Big +1 用于提及本地化错误消息,没有考虑翻译问题。
【解决方案2】:

我意识到这是一个老问题,但这可能会对处于类似情况的其他人有所帮助。

这里有需要封装到域模型中的行为和条件。

例如,我建议对特定长度有要求的 ProjectName 应该封装在 ValueObject 中。对某些人来说,这似乎有些过火,但在我们的领域模型中,我们几乎总是将原生类型(尤其是字符串)封装在一个 ValueObject 中。然后,这允许您在 ValueObject 的构造函数中滚动验证。

在构造函数中,您可以抛出与传入的参数冲突相关的异常。这是我们的 ZoneName 值对象之一的示例:

public ZoneName(string name)
{

    if (String.IsNullOrWhiteSpace(name))
    {
        throw new ArgumentNullException("Zone Name is required");
    }

    if (name.Length > 33)
    {
        throw new ArgumentException("Zone name should be less than 33 characters long");
    }

    Name = name;
}

现在,该 ValueObject 的使用者可以在调用构造函数之前执行自己的验证,也可以不执行,但无论哪种方式,您的不变量都将与您的模型设计保持一致。

我们在您的领域模型中构建验证规则,然后在您的 UI 中使用它们的一种方法是使用 Mediatr 模块,该模块使用一个模型输入,一个模型输出模式,并允许您为每个模型定义验证器查询或命令模型。这些是使用 FluentValidation 定义的。然后,您可以将提供程序添加到 MVC 中的 ModelValidatorProviders。在 https://github.com/jbogard/ContosoUniversity/tree/master/src/ContosoUniversity 处查看 JBogards ContosoUniversity 示例并查看 DependancyResolution 文件夹 DefaultRegistry.cs。

您的其他产品示例必须存在才能链接到项目。在我看来,这听起来像是域服务将是促进 2 个有界上下文之间合作的最佳选择?域服务将确保不变量在有界上下文中保持一致。该域服务不会公开公开,因此您需要一个 ApplicationService 或 CQRS 类型的接口,它将该 DomainService 作为依赖项,允许 DomainService 执行所需的操作。 DomainService 应该包含域行为,而应用程序服务应该只是调用该函数的促进者。然后,您的 DomainService 将引发异常,而不是导致不一致或无效的不变量。

您最终应该处于没有重复验证的位置,或者至少您永远不会遇到无效的不变量,因为在某些时候没有执行验证,因为验证总是在您的域模型中处理。

【讨论】:

    【解决方案3】:

    虽然描述性错误消息似乎更多地与表示相关而不是业务,但描述性错误消息实际上体现了域模型中包含的业务规则——并且在抛出任何类型的异常时,最好传递一些描述性的信息。此消息可以重新抛出层以最终显示给用户。

    现在,当涉及到抢先验证(例如允许用户仅键入某些字符或从某个范围的选项中进行选择的小部件)时,实体可能包含一些返回动态生成的正则表达式的常量或方法可以由视图模型使用,然后由小部件实现。

    【讨论】:

      猜你喜欢
      • 2011-06-16
      • 1970-01-01
      • 2013-12-21
      • 2021-11-27
      • 2016-09-26
      • 2010-10-13
      • 2020-08-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多