Vin*_*ves 19 c# architecture domain-driven-design repository fluentvalidation
我正在使用FluentValidation来验证我的服务操作.我的代码看起来像:
using FluentValidation;
IUserService
{
void Add(User user);
}
UserService : IUserService
{
public void Add(User user)
{
new UserValidator().ValidateAndThrow(user);
userRepository.Save(user);
}
}
Run Code Online (Sandbox Code Playgroud)
UserValidator实现FluentValidation.AbstractValidator.
DDD表示域层必须与技术无关.
我正在做的是使用验证框架而不是自定义异常.
将验证框架放在域层中是一个坏主意吗?
pla*_*alx 41
好吧,我发现你的设计存在一些问题,即使你通过声明一个IUserValidator
界面来保护你的域名不受框架影响.
起初,似乎这会导致与存储库和其他基础设施问题相同的抽象策略,但我认为存在巨大差异.
在使用时repository.save(...)
,您实际上并不关心从域角度来看所有的实现,因为如何持久化事物不是域关注的问题.
但是,不变执行是一个领域关注点,你不应该深入挖掘基础设施细节(UserValidtor
现在可以看作是这样),看看它们包含什么,这基本上是你最终要做的事情,如果你从那条路走下去规则将以框架术语表达,并且将存在于域外.
为什么要住在外面?
domain -> IUserRepository
infrastructure -> HibernateUserRepository
domain -> IUserValidator
infrastructure -> FluentUserValidator
Run Code Online (Sandbox Code Playgroud)
也许你的设计存在一个更基本的问题,如果你坚持这个学校,你甚至不会问这个问题:永远有效的实体.
从这个角度来看,不变执行是域实体本身的责任,因此甚至不能在没有效的情况下存在.因此,不变规则简单地表示为契约,并且当违反这些规则时抛出异常.
这背后的原因是很多错误都来自于物体处于他们本不应该存在的状态.为了揭示我从Greg Young那里读到的一个例子:
我们的建议,我们现在有一个
SendUserCreationEmailService
,需要一个UserProfile
......我们怎么能在服务,合理化Name
是不是null
?我们再检查一下吗?或者更可能......你只是不费心去检查和"希望最好",你希望有人在发送给你之前打扰验证它.当然使用TDD我们应该编写的第一个测试之一是,如果我发送一个null
名称应该引发错误的客户.但是,一旦我们开始一遍又一遍地编写这些类型的测试,我们就会意识到......"如果我们从不允许名称变为空,我们将不会进行所有这些测试" - Greg Young在http://jeffreypalermo.com上发表评论/博客/的,谬误的最总是有效的实体/
现在不要误解我,显然你不能以这种方式强制执行所有验证规则,因为某些规则特定于禁止该方法的某些业务操作(例如保存实体的草稿副本),但这些规则不被查看与不变执行相同的方式,这是在每个方案中应用的规则(例如,客户必须具有名称).
如果我们现在查看您的代码并尝试应用始终有效的方法,我们清楚地看到该UserValidator
对象没有它的位置.
UserService : IUserService
{
public void Add(User user)
{
//We couldn't even make it that far with an invalid User
new UserValidator().ValidateAndThrow(user);
userRepository.Save(user);
}
}
Run Code Online (Sandbox Code Playgroud)
因此,此时域中没有FluentValidation的位置.如果您仍然不相信,请问自己如何整合价值对象?你是否会在每次实例化时UsernameValidator
验证一个Username
值对象?显然,这没有任何意义,使用价值对象很难与非常有效的方法相结合.
这实际上是我努力的事情,我一直在问自己一段时间(而且我仍然不完全相信我会说些什么).
基本上,我所理解的是,收集和返回错误不是域的工作,这是UI关注点.如果无效数据使其达到域名,那么它只会引发您的注意.
因此,像FluentValidation这样的框架将在UI中找到它们的自然之家,并且将验证视图模型而不是域实体.
我知道,似乎很难接受会有一定程度的重复,但这主要是因为你可能是像我一样处理UI和域的全栈开发人员,而事实上那些可以而且应该被查看作为完全不同的项目.此外,就像视图模型和域模型一样,视图模型验证和域验证可能类似,但用途不同.
此外,如果你仍然担心干嘛,有人曾告诉我代码重用也是"耦合",我认为这个事实在这里特别重要.
我不会在这里重新解释这些,但有各种方法来处理域中的延迟验证,例如Ward Cunningham在其Checks模式语言中描述的规范模式和延迟验证方法.如果您拥有Vaughn Vernon的"实施域驱动设计"一书,您还可以阅读第208-215页.
验证是一个非常困难的主题,证据是,截至今天,人们仍然不同意如何做.有很多因素,但最终你想要的是一个实用,可维护和富有表现力的解决方案.您不能总是纯粹主义者,并且必须接受一些规则将被破坏的事实(例如,您可能必须在实体中泄漏一些不显眼的持久性细节以便使用您选择的ORM).
因此,如果您认为您可以忍受一些FluentValidation详细信息进入您的域并且更实用的事实,那么从长远来看,我无法确定它是否会带来更多弊大于利.不会.