在DDD中,唯一性检查应该放在哪里?

14

我正在进行我的第一个领域驱动设计项目,我认为我理解了实体、数据访问对象及其关系的基本角色。我有一个基本的验证实现,它将每个验证规则与其关联的实体一起存储。对于仅适用于当前实体的规则,这很好运作,但当需要其他数据时就会出现问题。例如,如果我有一个限制条件,即用户名必须唯一,当存在具有当前名称的用户时,我希望 IsValid() 调用返回 false。

然而,我没有找到任何简洁的方法来保留此验证规则在实体本身上。我希望在实体上有一个名为 IsNameUnique 的函数,但大多数解决方案都要求我注入一个用户数据访问对象。这个逻辑应该在外部服务中吗?如果是这样,如何仍然将逻辑保留在实体本身中?还是这应该超出用户实体之外的东西?

谢谢!

3个回答

4

我喜欢Samuel的回答,但为了简单起见,我建议使用Specification实现。 你可以创建一个返回布尔值的规范来查看对象是否符合某些条件。将IUserRepository注入到规范中,检查是否已存在具有该名称的用户,并返回布尔结果。

public interface ISpecification<T>
{
  bool IsSatisfiedBy(TEntity entity);
}

public class UniqueUsernameSpecification : ISpecification<User>
{
  private readonly IUserRepository _userRepository;

  public UniqueUsernameSpecification(IUserRepository userRepository)
  {
    _userRepository = userRepository;
  }

  public bool IsSatisfiedBy(User user)
  {
    User foundUser = _userRepository.FindUserByUsername(user.Username);
    return foundUser == null;
  }
}

//App code    
User newUser;

// ... registration stuff...

var userRepository = new UserRepository();
var uniqueUserSpec = new UniqueUsernameSpecification(userRepository);
if (uniqueUserSpec.IsSatisfiedBy(newUser))
{
  // proceed
}

12
无法正常工作。在检查和保存之间,其他线程可能会插入数据。 - dariol

2
在DDD中,有一个叫做聚合的概念。它基本上负责应用程序的一致性。
在我看来,特别是在这种情况下,我猜CustomerRepository会被放在类似于“客户聚合”之内,其中Customer类是聚合根。
然后根将负责做所有这些事情,没有其他人能够访问CustomerRepository选项。还有一些可能性:
- 如果名称不唯一,CustomerRepository可以抛出异常(而Customer会捕获并返回错误,或者类似于此)。 - CustomerRepository可以有一个IsValidUserName(),Customer在执行任何其他操作之前会调用它。 - 你能想到的其他任何选项。

2
我认为这已经超出了DDD的范围。
使用DDD,您可以聚合产生有用的模型的事件。但是,这些模型之间的数据关系不一定可能存在。
您使用哪种一致性模型?
如果您可以在ACID事务中提交多个事件,则可以确保原子地更改一组聚合。
如果您使用最终一致性模型,则可能无法实际验证这些内容,直到稍后。当您这样做时,您可能需要对已经发生但不再有效的事情进行补偿。
唯一性必须在上下文中回答。如果您的模型很小(在数千个以下),则可以让一个聚合表示您想要唯一的值集。假设组聚合交易可行。
如果不行,那么您只需将模型投影到支持唯一性约束的数据库中即可。如果此投影失败,则必须返回到聚合并将其标记为无效。同时,您需要考虑故障。这就是分布式长时间运行进程(如saga模式)可以派上用场的地方,但也需要考虑其他事项。
总之,如果您不能使用具有强一致性保证的存储模型,则所有内容都会变得更加复杂。话虽如此,在分布式环境中管理故障的好方法和良好的模式存在,但它们将问题颠倒了一些,因为现在您需要考虑故障,在每个阶段都需要考虑故障,这是一件好事,但它需要更多的时间投入。

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