在分层应用程序中验证

时间:2012-05-01 20:44:26

标签: c# model-view-controller validation repository domain-driven-design

我想知道在ASP.NET MVC应用程序中验证数据库约束(例如UNIQUE)的最佳方法是什么,在构建时考虑到DDD,底层是Application Layer(应用程序服务),Domain Layer(域模型)和基础设施层(持久性逻辑,日志记录等)。

我一直在查看大量DDD样本,但其中许多未提及的是如何在存储库中进行验证(我认为这是此类验证适合的地方)。如果您知道有任何样本,请分享,非常感谢。

更具体地说,我有两个问题。 您将如何执行实际验证?您是否通过查询数据库明确检查客户名称是否已存在,或者您是否尝试将其直接插入数据库并捕获错误(如果有的话)(似乎很乱) ?我更喜欢第一个,如果选择这个,是应该在存储库中完成,还是应该是应用程序服务的工作?

当检测到错误时,如何将其传递给ASP.NET MVC以便可以很好地通知用户错误?最好使用ModelStateDictionary以便错误很容易在表格上突出显示。

在Microsoft Spain的N-Lyered应用程序中,他们使用IValidatableObject接口,最简单的属性验证放在实体本身,例如:

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
    var validationResults = new List<ValidationResult>();

    if (String.IsNullOrWhiteSpace(this.FirstName))
        validationResults.Add(new ValidationResult(Messages.validation_CustomerFirstNameCannotBeNull, new string[] { "FirstName" }));

    return validationResults;
}

在实体持久化之前,将调用Validate消息以确保属性有效:

void SaveCustomer(Customer customer)
{
    var validator = EntityValidatorFactory.CreateValidator();

    if (validator.IsValid(customer)) //if customer is valid
    {
        _customerRepository.Add(customer);
        _customerRepository.UnitOfWork.Commit();
    }
    else
        throw new ApplicationValidationErrorsException(validator.GetInvalidMessages<Customer>(customer));
}

然后可以在MVC应用程序中捕获ApplicationValidationErrorsException,并且可以解析验证错误消息并将其插入ModelStateDictionary

我可以将所有验证逻辑添加到SaveCustomer方法中,例如查询数据库,检查客户是否已经存在使用给定列(UNIQUE)。 也许这没关系,但我宁愿validator.IsValid(或类似的东西)会为我做这个,或者在Infrastructure层再次执行验证(如果它属于这里,我不确定)。< / p> 你怎么看?你怎么做呢?我非常有兴趣深入了解分层应用程序中的不同验证技术。


可能的解决方案#1

如果验证逻辑无法在表示层中完成(如Iulian Margarintescu建议)并且需要在服务层中完成,那么如何将验证错误传递到表示层?

Microsoft有一个建议here(参见清单5)。您如何看待这种方法?

2 个答案:

答案 0 :(得分:2)

你提到DDD,但DDD还有很多实体和存储库。我假设您熟悉Eric Evans先生的书“领域驱动设计”,我强烈建议您重新阅读有关战略设计和有限背景的章节。此外,埃文斯先生还有一个非常好的演讲,名为“自从这本书以来我对DDD的了解”,你可以找到here。来自Greg Young或Udi Dahan的关于SOA,CQRS和事件采购的谈话也包含有关DDD和应用DDD的大量信息。我必须警告你,你可能会发现一些会改变你对DDD应用方式的看法。

现在提出有关验证的问题 - 一旦用户在“名称”字段中键入内容并尝试建议替代方法,一种方法可能是查询数据库(使用指向应用服务的Ajax调用)如果他输入的那个已存在,请输入姓名。当用户提交表单时,尝试在db中插入记录并处理任何重复的键异常(在存储库或应用程序服务级别)。由于您已经提前检查重复项,因此您获得异常的情况应该相当罕见,因此任何体面的“我们很抱歉,请重试”消息应该这样做,除非您有很多用户他们可能永远不会看到它。

来自Udi Dahan的

This post也有一些关于接近验证的信息。请记住,这可能是您对业务施加的约束,而不是业务对您施加的约束 - 也许它为业务提供了更多价值,允许具有相同名称的客户进行注册,而不是拒绝它们。

还要记住DDD更多的是关于业务而不是技术。您可以执行DDD并将应用程序部署为单个程序集。在数据库之上的存储库之上的实体之上的服务之上的客户端代码层已经以“好”设计的名义被滥用了很多次,没有任何理由说明为什么它是一个好的设计。

我不确定这会回答你的问题,但我希望它会引导你自己找到答案。

答案 1 :(得分:0)

  

我想知道验证数据库约束的最佳方法是什么(例如UNIQUE)

     

如果选择这个,是应该在存储库中完成,还是应该是应用程序服务的工作?

这取决于您的验证内容。

如果它是您尝试验证的聚合根创建 - 那么没有什么比应用程序本身“持有”它更全局了。在这种情况下,我直接在存储库中应用验证。

如果它是一个实体,它将存在于聚合根上下文中。在这种情况下,我将验证聚合根本身中的实体唯一性与此特定聚合根中的所有其他实体。实体/根中的值对象也是如此。

P.S。存储库是一种服务。不要将服务视为必需的通用存储,但很难正确命名代码。命名很重要。 “助手”,“经理”,“共同”,“公用事业”等名称也是如此 - 它们几乎毫无意义。

此外 - 您不需要使用模式名称污染代码库:AllProducts&gt; ProductRepository; OrderRegistrator&gt; OrderService; order.isCompleted&gt; IsOrderCompletedSpecification.IsSatisfiedBy。

  

更具体地说,我有两个问题。你会如何进行实际验证?您是否通过查询数据库明确检查客户名称是否已存在,或者您是否尝试将其直接插入数据库并捕获错误(如果有的话)(似乎很乱)?

我会查询数据库。虽然,如果高性能是一个问题,客户名称可用性只是数据库应该强制实施的东西 - 我会依赖数据库(少一次往返)。

  

当检测到错误时,如何将其传递给ASP.NET MVC,以便能够很好地通知用户错误?最好使用ModelStateDictionary,以便在表单上轻松突出显示错误。

通常使用异常来控制应用程序流并不是一个好主意,但是,由于我想强制UI只显示可以完成的可用事项,所以我只是在例如验证失败的情况下抛出异常。在UI层中,有一个处理器可以整齐地拾取它并在html中吐出。

此外 - 了解命令的范围很重要(例如,产品订购命令可能会检查2件事情 - 如果客户不是债务人且产品是否在店内)。如果命令具有多个关联验证,则应将它们耦合在一起,以便UI同时接收它们。否则会导致恼人的用户体验(在尝试一遍又一遍地命令该死的产品时会遇到多个错误)。