DDD中的唯一性验证

13

我有一个与DDD中唯一性检查相关的问题。我知道stackoverflow上已经有一些关于这个问题的提问了,但它们并没有真正回答我的疑虑。

在更新/插入数据库时,聚合根是否可以持有仓储(repository)的引用来检查唯一性?还是这项任务由应用程序服务(application service)而非领域模型(domain model)完成?

假设我想检查用户(User)模型的用户名在用户注册时是否唯一。我可以考虑以下几种方法:

  • 用户(User)模型引用UserRepository,在Validate()中进行唯一性检查
  • 创建一个领域服务(domain service)来进行唯一性检查,使用UserRepository (对我来说这似乎有点奇怪,因为我认为通常只有涉及多个领域模型的情况下才会使用领域服务)
  • 在领域层创建一个规约对象(specification object),引用UserRepository来封装唯一性检查规则,并且应用程序服务(application service)层在更新/插入之前使用此对象进行检查。

如果我使用依赖注入(dependency injection),我仍然在思考如何将UserRepository注入到第一种方法中的用户(User)对象、第二种方法中的领域服务(domain service)或第三种方法中的应用程序服务(application service)中,因为在任何情况下,对于用户(User)/领域服务(domain service)/规约对象(specification object),我都需要手动实例化这些对象,所以我似乎唯一的选择是使用IoC中的服务定位器(service locator)来获取实例。但是服务定位器是反模式,所以我想避免使用它。

非常感谢任何样例代码。


2
我认为你应该在服务层实现这个功能。更具体地说,为特定类型的业务逻辑(即你的命令)定义一个验证器类,并在装饰器中处理验证,然后将其包装在服务类周围。 - Steven
这个有帮助吗?https://dev59.com/tmct5IYBdhLWcg3wnurg#11958251 - Dmitry
@Steven,那我应该创建一个UserValidator来实现在服务层中检查用户吗?这是否被认为是业务规则(用户名的唯一性)泄露到域层之外? - David N
1个回答

11

我认为检查唯一性是存储库的责任。存储库知道所有聚合根,因为它应该模拟领域集合,所以向存储库询问唯一性就很自然(就像你从HashMap中期望的那样)。

// repository
interface Users {
  // implementation executes SQL COUNT in case of relation DB
  bool IsNameUnique(String name);

  // implementation will call IsNameUnique and throw if it fails
  void Add(User user);
}

在某种意义上,这是一个不完美的抽象,因为在多用户环境中,这需要在数据存储方面进行强制执行(例如唯一的SQL约束或锁定)。

应该避免从用户聚合调用IsNameUnique方法,我会将此调用移动到应用程序或域服务中,具体取决于您的应用程序结构的其余部分。

有关替代方法,请参见CQRS架构中的唯一性验证


那么您建议采用上述第2种方法,创建一个域服务来调用存储库以检查唯一性?在DDD中,像这样在域层创建验证服务是常见的方式吗?我对DDD还不太了解。 - David N
无论是从域还是应用程序服务中使用此存储库,取决于您的用例,只要不从聚合中使用此方法,任何一种都可以。 - Dmitry
谢谢,最终我决定采用这种方法:http://lostechies.com/jimmybogard/2007/10/24/entity-validation-with-visitors-and-extension-methods/ - David N

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接