8

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

我一直在查看很多 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 方法中,例如使用给定列(唯一列)查询数据库检查客户是否已经存在。也许这没关系,但我宁愿validator.IsValid(或类似的东西)为我做这件事,或者在基础设施层再次执行验证(如果它属于这里,我不确定)。

你怎么看?你怎么做呢?我对深入了解分层应用程序中的不同验证技术非常感兴趣。


可能的解决方案#1

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

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

4

2 回答 2

2

您提到了 DDD,但 DDD 不仅仅是实体和存储库。我假设您熟悉 Eric Evans 先生的《领域驱动设计》一书,我强烈建议您重新阅读有关战略设计和有界上下文的章节。此外,Evans 先生有一个非常精彩的演讲,名为“自从这本书以来我对 DDD 的了解”,您可以在此处找到。Greg Young 或 Udi Dahan 关于 SOA、CQRS 和事件溯源的讨论也包含很多关于 DDD 和应用 DDD 的信息。我必须警告您,您可能会发现一些事情会改变您对应用 DDD 的看法。

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

Udi Dahan的这篇文章也有一些关于接近验证的信息。请记住,这可能是您对企业施加的约束,而不是企业对您施加的约束——也许它为企业提供了更多价值,允许同名客户注册,而不是拒绝他们。

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

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

于 2012-05-02T02:59:19.993 回答
0

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

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

这取决于您要验证的内容。

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

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

ps 存储库是一项服务。不要将服务视为通用存储,以获取必要但难以正确命名的代码。命名很重要。“Helpers”、“Managers”、“Common”、“Utilities”等名称也是如此——它们几乎毫无意义。

此外 - 您不需要使用模式名称污染您的代码库:AllProducts > ProductRepository;订单注册器 > 订单服务;order.isCompleted > IsOrderCompletedSpecification.IsSatisfiedBy。

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

我会查询数据库。虽然,如果高性能是一个问题并且客户名称可用性是数据库应该强制执行的唯一事情 - 我会依赖数据库(减少往返次数)。

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

通常使用异常来控制应用程序流不是一个好主意,但是,由于我想强制 UI 只显示可以完成的可用事情,所以我只是在验证失败时抛出异常。在 UI 层中,有一个处理程序可以巧妙地将其拾取并以 html 形式输出。

此外 - 了解什么是命令的范围很重要(例如,产品订购命令可能会检查两件事 - 如果客户不是债务人以及产品是否在商店中)。如果命令有多个关联的验证,这些应该耦合在一起,以便 UI 可以同时接收它们。否则会导致烦人的用户体验(在尝试一遍又一遍地订购该死的产品时经历多个错误)。

于 2012-05-03T08:26:06.380 回答