Domain driven design DDD-建模用户,其联系信息在整个系统中必须是唯一的

Domain driven design DDD-建模用户,其联系信息在整个系统中必须是唯一的,domain-driven-design,cqrs,domain-data-modelling,Domain Driven Design,Cqrs,Domain Data Modelling,我需要一些关于为身份和访问域建模用户的澄清。用户域模型有一个联系信息实体(因为它是可变的实体),客户可以注册电话号码,但可以在需要时选择更改 客户曾经使用过的电话号码永远不会被任何其他用户使用。因此,我认为该模型必须允许查询电话号码表(因为它与客户是多对一的,因为旧号码被停用并存档) 如果创建domainservice是可以的,那么存储库应该是什么,因为没有标识聚合。 在这些情况下,我有一个customer(user)聚合,但为了允许查询所有用户以查看客户提供的电话号码是否已被其他人使用,聚合应

我需要一些关于为身份和访问域建模用户的澄清。用户域模型有一个联系信息实体(因为它是可变的实体),客户可以注册电话号码,但可以在需要时选择更改

客户曾经使用过的电话号码永远不会被任何其他用户使用。因此,我认为该模型必须允许查询电话号码表(因为它与客户是多对一的,因为旧号码被停用并存档)

如果创建domainservice是可以的,那么存储库应该是什么,因为没有标识聚合。
在这些情况下,我有一个customer(user)聚合,但为了允许查询所有用户以查看客户提供的电话号码是否已被其他人使用,聚合应该是什么,或者我可以编写一个DomainService,直接查询数据库到phonenumber表以检查唯一性,如果我这样做违反了DDD原则,那么什么是更干净的替代方案。

另一种替代方案是创建一个聚合,使其明确表示您希望唯一约束保留的范围

举个(人为的)例子,一个电话号码在一个国家可能是唯一的,但在国际上可能不是唯一的。因此:

// An Aggregate Root
public class Country {

  // Store a lookup structure (userId, phoneNumber) here

  public void addUser(userId, phoneNumber) {
    // check phone uniqueness here
  }

  public void changeUserPhone(userId, phoneNumber) {
    // check phone uniqueness here
  }
}
因为您使用的是CQR,所以电话号码在单独的聚合中并不重要,因为在查询端,读取模型将用户和他们的电话号码重新组合在一起


这也适用于该方法,因为您有一个创建用户(用户可能是AR)的起点,而不仅仅是凭空创建它。

您可以让存储库检查电话号码是否存在,如果存在,然后抛出规则异常,否则保存更改。这里的关键是通过应用程序层注入存储库的实例,并在域层内运行规则

@Matt,谢谢,我知道您建议将相关数据插入非域表中,优化为读取,并从客户端读取表进行验证,但如何使用直接查询表的domainservice进行服务器端验证?正如对另一个问题的一个答案所示,有几种方法可以解决这个问题。要么保留一个注册电话号码的小型读取模型,供写入端在执行命令时参考并对其执行验证,要么(我的首选方式)让它发生,让流程经理发现重复/中断的验证并采取纠正措施。客户机应始终期望执行其发出的任何命令,所以你说的是一小部分情况下的一致性破坏。这意味着当
电话号码
被修改时,
国家
必须在与
用户
相同的事务中被修改。否?这提醒了我一个问题:@plalx不一定。如果
PhoneNumber
在系统的重要部分必须是唯一的,这可能意味着它是一个重要的领域概念。因此,修改它并不是一个良性的行为,它可能有自己无处不在的语言术语和自己的命令,这些语言术语和命令总是自己执行的,而不是作为普通用户修改的一部分。你能对此进行进一步的扩展吗,也许与您如何处理与手机关联的用户的创建以及如何处理用户的手机修改有关。我在查看将要使用的所有组件的流程时遇到了一些问题。创建:在客户端表单上,键入时会进行电话号码验证,可能是查询CountryRepository或包含已获取电话号码列表的读取模型。然后,您必须将其带到服务器端(并重新验证)。将发出CreateUser命令,或者发出CreateUserPhone和CreateUser命令。就交易而言,它可以在一个横跨国家和用户集合的交易中完成,或者如果电话号码部分失败,则可以在两个具有补偿操作的交易中完成。