我在这里听到您对我们迄今为止所采用的方法的看法.我们还处于开发过程的早期阶段,所以我们仍然可以改变它.验证对于此应用程序和我们的客户非常重要,因此我们需要找到最佳方式.让我来描述一下到目前为止我们做了什么……
我们正在构建这个将被不同客户使用的应用程序.我们不控制所有客户端,因此对所有层的验证都有严格的要求.我们控制一些客户端应用程序,一个是~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时,除非在验证中的某处出现错误,否则不应引发参数异常.因此,您可以说域模型中的这些参数检查充当额外的安全层.另一方面,如果客户端调用一个调用Customer对象上的方法的服务方法,该方法在当前状态下不可用(因此它应该失败),则可以引发InvalidOperationException.
你怎么看?如果一切听起来都不错,那么当出现故障时,如何通过WCF适当地通知用户?无论是ArgumentException,InvalidOperationException还是包含错误列表的异常(在使用CustomerValidator类验证客户对象后由ApplicationService抛出).我应该以某种方式捕获所有这些异常并将它们转换为WCF引发的一些常规故障异常,因此客户端可以对它做出反应并通知用户发生了什么?
我很想听听你对我们方法的看法.我们正在构建这个相当大的应用程序,我们真的想找到一种执行验证的好方法.在我们的应用程序中有一些非常重要的部分,其中数据的正确性非常重要,因此验证非常重要!
最佳答案 我个人认为域名一致性应由域名处理.所以不需要各种各样的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对客户端的表达不太明显.
有了明确的例外,客户只需要尝试/捕获他们可以处理的异常(并记住向用户提供异常是一种处理它的方法).