Mar*_*kus 5 oop domain-driven-design
我对ddd中的验证方法有疑问.我已经阅读了相当有争议的意见.有人说这应该是实体生活,有人说这应该放在实体中.我正试图找到一种我可以遵循的方法.
例如,假设我有User实体,其中包含电子邮件和密码.用户有方法注册(电子邮件,密码).电子邮件和密码验证应该放在哪里?我个人认为它应该在Register()方法中.但是这种方法可能会导致User类与验证内容混乱.一种方法可能是在单独的策略对象中提取电子邮件和密码规则,并仍然从Register()方法调用它们.
您对DDD的验证方法有何看法?
首先,我认为验证在某种程度上是一个滑的主题,部分原因在于它需要考虑的各种情况.最终,将在各种应用层执行验证规则.至少,域对象中应该有标准的防护.这些只是常规的前置条件和参数检查,应该是任何精心设计的对象的一部分,并符合您对该Reigster
方法的意见.正如lazyberezovsky所说,这是为了防止物体进入无效状态.我支持永远有效的学校.我认为如果需要将实体持久化为无效状态,则应为此目的创建新实体.
然而,仅此方法的一个问题是通常需要将这些验证规则导出到其他层,例如表示层.此外,在表示层中,规则需要以不同的格式进行格式化.它们需要一次性呈现,并可能转换为另一种语言,例如JavaScript,以便立即进行客户端反馈.尝试从类引发的异常中提取验证规则可能很困难或不切实际.或者,可以在表示层重新创建验证规则.这更简单,虽然可能违反DRY,但它允许规则依赖于上下文.特定工作流可能需要不同于实体本身强制执行的验证规则.
所描述的方法的另一个问题是可能存在超出实体范围的验证规则,并且这些规则必须与其他规则一起合并.例如,对于用户注册,另一个规则是确保电子邮件地址是唯一的.托管适用用例的应用程序服务通常会强制执行此规则.但是,它还必须能够将此规则导出到其他层,例如演示.
总的来说,我尝试将尽可能多的约束检查放入实体本身,因为我认为实体应该始终有效.有时可以设计规则框架,使其可用于引发异常并可导出到外层.其他时候,简单地跨层复制规则更容易.
实际上,用户的有效性是依赖于上下文的。用户可以是有效的(姓名和电子邮件有效),但注册操作可能无法执行。为什么?因为可能已经存在同名的用户。因此,在某些上下文中看起来有效的用户在注册上下文中可能无效。
因此,我将部分验证移至实体或值对象(例如 Mail 对象)中,以避免实体的明显无效状态(例如具有null
名称的用户)。但上下文相关的验证应该存在于该上下文中。所以,如果我注册用户:
Mail mail = new Mail(blahblahblah); // validates if blah is valid email
User user = new User(name, mail); // validates if name is valid and mail not null
// check if there already exist user with such name or email
repository.Add(user);
Run Code Online (Sandbox Code Playgroud)
另外我认为该方法Register
应该是某些域服务(如 MembershipService)的方法,因为它肯定应该使用某些存储库。