关于我们在具有多个客户端的分布式应用程序中验证方法的想法

时间:2013-03-23 16:43:59

标签: c# wcf validation domain-driven-design

我在这里听到您对我们迄今为止进行验证的方法的看法。我们还处于开发过程的早期阶段,所以我们仍然可以改变它。验证对于此应用程序和我们的客户非常重要,因此我们需要找到最佳方式。让我来描述我们到目前为止所做的工作......

我们正在构建将由不同客户端使用的此应用程序。我们不控制所有客户端,因此对所有层的验证都有严格的要求。我们控制一些客户端应用程序,一个是~100个用户使用的WPF应用程序。从该应用程序,工作流程如下:

|                     Client                   |                                 Backend Service                             |
ViewModel -> ClientRepository -> ServiceClient -> Service (WCF) -> ApplicationService -> DomainModel -> Repository -> Database

我们将以下内容视为执行验证的候选对象。

  • 客户端:ViewModel验证,用于支持具有必填字段,长度等的UI
  • 后端:服务请求DTO验证,因为我们不能依赖客户端始终提供100%有效值。
  • 后端:域模型实体验证。我们不希望我们的实体最终处于无效状态,因此每个实体在执行操作时都会包含不同的检查。
  • 后端:数据库验证,例如失败的约束(FK,唯一性,长度等)

客户端ViewModel验证非常明显,对于我们自己的客户,在到达服务之前应该在那里纠正尽可能多的错误。不能代表消费我们服务的其他应用程序,并且应该假设最坏的情况。

服务请求DTO应该主要针对第三方应用程序的情况和我们自己客户端的错误进行验证。确保请求正确,可以防止在处理请求时出现错误,从而确保更有效的服务。与ViewModel验证一样,这取决于不同属性的必填字段,长度和格式(例如电子邮件)。

域模型中的实体本身应确保它们始终具有完全有效的属性/属性,我们正在实现此目的,以Customer实体为例。

public class Customer : Entity
{
    private Customer() : base() { }

    public Customer(Guid id, string givenName, string surname)
        : this(id, givenName, null, surname) { }

    public Customer(Guid id, string givenName, string middleName, string surname)
        : base(id)
    {
        if (string.IsNullOrWhiteSpace(givenName))
            throw new ArgumentException(GenericErrors.StringNullOrEmpty, "givenName");
        if (string.IsNullOrWhiteSpace(surname))
            throw new ArgumentException(GenericErrors.StringNullOrEmpty, "surname");

        GivenName = givenName.Trim();
        Surname = surname.Trim();

        if (!string.IsNullOrWhiteSpace(middleName))
            MiddleName = middleName.Trim();
    }
}

现在虽然这可以确保属性有效,但CustomerValidator类会验证Customer类的整体,确保它处于有效状态,并且不仅具有有效属性。 CustomerValidator是使用FluentValidation框架实现的。在将客户对象提交到数据库之前,在应用程序服务中调用它。

到目前为止,您如何看待我们的方法?

我有点担心的是,异常的使用是在各处抛出的。例如。 ArgumentException上面的例子,但是如果调用某个方法在对象的当前状态下是不允许的话,也会InvalidOperationException

希望这些异常很少被抛出,因为服务请求DTO已经过验证,因此我认为它可能没问题?例如,当验证服务请求DTO时,除非在验证中的某处出现错误,否则不应引发参数异常。因此,您可以说域模型中的这些参数检查充当额外的安全层。另一方面,如果客户端调用一个调用客户对象上当前状态不可用的方法的服务方法(因此它应该失败),则可以引发InvalidOperationException

你怎么看?如果一切听起来都不错,那么当出现故障时,如何通过WCF适当地通知用户?是ArgumentExceptionInvalidOperationException,还是包含错误列表的异常(在使用CustomerValidator类验证客户对象后由ApplicationService抛出)。我应该以某种方式捕获所有这些异常,并将它们变成WCF引发的一些常规故障异常,因此客户端可以对它做出反应并告知用户发生了什么?

我很想听听你对我们方法的看法。我们正在构建这个相当大的应用程序,我们真的想找到一种执行验证的好方法。在我们的应用程序中有一些非常重要的部分,其中数据的正确性非常重要,因此验证很重要!

2 个答案:

答案 0 :(得分:2)

我个人认为域名一致性应由域名处理。所以不需要CustomerValidator种类。

至于例外情况,您应该考虑ArgumentNullException,它们应该是普遍存在的语言的术语(有关更深入的解释,请参阅http://epic.tesio.it/2013/03/04/exceptions-are-terms-ot-the-ubiquitous-language.html)。

顺便说一下,即使您的所有DTO都已经过验证,也不应该从域中删除正确的验证。商业不变量是它自己的责任。

至于性能:异常有计算成本,但在我看到的大多数DDD场景中,它们都不是问题。特别是当命令来自人类时,它们不是问题。

修改
验证始终是域的责任。获取ISIN值对象:由构造函数决定its own invariants是否通过抛出适当的异常。在编码良好的域中,您无法保存无效对象的实例。因此,您不需要任何验证器来累积错误。

同样,当且仅当它们是获取实例的唯一方式时,工厂才能确保业务不变量。技术不变量,例如db column lenght,应该在域外,因此工厂可能是他们的好位置。这也具有启用异常链接的优点:SqlExceptions对客户端的表达不太明显。

有了明显的例外,客户只需要尝试/捕获他们可以处理的异常(并记住向用户提供异常是一种处理它的方法)。

答案 1 :(得分:0)

数据进入您的系统是有意义的。这可以是动作参数(视图模型)或服务层中的参数的形式。 此时必须始终进行超验证(使所有内容都可以为空,然后禁止空值,检查整数上的负数等),并在这些入口点设置所有内容都是100%正确的。然后,除非验证是针对特定边缘情况,否则系统的其余部分不必担心它。 客户端验证很好,但您绝不能完全依赖它。验证中有时可能会断开连接。此外,没有承诺调用您的操作的客户是您认为他们的客户(例如,我们已经更改了网址中的查询参数以查看会发生什么)。

我发布的代码存在的问题是,您的域中的数据可能有效,也可能无效。如果您始终在流程的外部边界执行验证,则无需担心。此外,你永远不会想知道,“我在哪里进行验证?”