Domain driven design DDD—存储库是否可以通过标识符以外的其他方式获取聚合?

Domain driven design DDD—存储库是否可以通过标识符以外的其他方式获取聚合?,domain-driven-design,ddd-repositories,Domain Driven Design,Ddd Repositories,我将用户建模为聚合根,用户由标识符值对象和电子邮件值对象组成。这两个值对象都可以唯一地标识用户,但是允许更改电子邮件,并且标识符不能更改 在我看到的大多数DDD示例中,聚合根的存储库仅按标识符获取。添加另一个通过电子邮件获取存储库的方法是否正确?我的建模是否很差?我会将此功能放在特定于您的用户对象的服务/业务层中。不是每个对象都有电子邮件标识符。这看起来更像是业务逻辑,而不是存储库的职责。我相信你已经知道这一点,但这很好地解释了我所说的 我不建议这样做,但您可以为用户提供一个特定的存储库实现,它

我将用户建模为聚合根,用户由标识符值对象和电子邮件值对象组成。这两个值对象都可以唯一地标识用户,但是允许更改电子邮件,并且标识符不能更改


在我看到的大多数DDD示例中,聚合根的存储库仅按标识符获取。添加另一个通过电子邮件获取存储库的方法是否正确?我的建模是否很差?

我会将此功能放在特定于您的用户对象的服务/业务层中。不是每个对象都有电子邮件标识符。这看起来更像是业务逻辑,而不是存储库的职责。我相信你已经知道这一点,但这很好地解释了我所说的


我不建议这样做,但您可以为用户提供一个特定的存储库实现,它公开了一个GetByEmail(string emailAddress)方法,但我仍然喜欢这个服务理念。

我想说是的,存储库应该有一些方法,用于通过身份以外的其他方式检索聚合。然而,有一些微妙之处需要注意

许多存储库示例仅按ID检索的原因是,与聚合结构相结合的存储库无法满足所有查询需求。例如,如果您有一个查询,该查询调用聚合中的某些字段以及引用聚合和某些摘要数据的某些字段,则相应的聚合类不能用于表示此数据。相反,需要一个专门的解决方案。因此,查询职责与存储库分离。这有几个优点(查询可以由一个专用的非规范化存储提供),并且它是数据库的主要范例。在这种类型的体系结构中,只有当需要执行某些行为时,存储库才会检索域类。所有只读用例都由只读模型提供服务


我认为存储库使用GetByEmail方法是合适的,这是基于YAGNI和与复杂性的斗争。您可以允许您的应用程序随着需求的变化和增长而发展。您不需要立即跳转到CQR和单独的读/写存储。您可以从碰巧也有查询方法的存储库开始。唯一需要记住的是,当您需要调用实体上的某些行为时,应该尝试通过ID检索实体。

我同意eulerfx的回答:

你需要问问自己,为什么你需要使用一些东西来获得AR 除了身份证

我认为很明显,您没有ID,但您有一些其他唯一标识符,例如电子邮件地址

如果使用CQR,首先需要确定数据对域还是仅对查询存储重要。如果您要求数据100%一致,那么它会稍微改变一些事情。例如,如果要检查电子邮件地址是否存在以满足唯一约束,则需要100%的一致性。如果查询的数据在任何时候都过时,您可能会遇到问题

请记住,存储库代表一个排序集合。因此,如果您不需要在AR(命令端)上实际操作,但您已经决定在哪里使用您的域是合适的,那么您可以始终在存储库上使用
ContainsEMailAddress
;否则,也可以为域数据存储设置查询端,因为域数据存储(OLTP类型存储)是100%一致的,而查询存储(OLAP类型存储)可能最终是一致的,与具有单独查询存储的CQR一样。

添加另一种通过电子邮件获取存储库的方法是否正确?-我不会那样做。在我看来,存储库应该只有通过id获取、保存和删除的方法

我想问一下,为什么在命令处理程序中没有用户id,您希望在其中获取用户并对其调用域方法。我不知道您到底在做什么,但是对于登录/注册场景,我会做以下操作。当用户登录时,他会传递一个电子邮件地址和密码,您会执行一个查询来对用户进行身份验证-这不会使用域或存储库(仅用于命令),而是会使用一些查询实现,它将返回一些包含用户id的UserDto,从这一点上您就有了用户id。下一个场景是注册。创建新用户的命令处理程序将创建一个新的用户实体,然后用户需要登录

在我看到的大多数DDD示例中,都是一个用于聚合的存储库 root仅按标识符获取

我很想知道你看过哪些例子。根据DDD,存储库是

一种封装存储、检索和搜索行为的机制 它模拟一组对象

搜索显然包括根据各种条件获取根或根集合,而不仅仅是它们的ID


存储库是
getCustomerByMail()
getCustomerOver18()
getCustomerByCountry(…)
等的理想场所。

这意味着您无法以通用方式(IRepository)将存储库传递到命令处理程序,但它需要是一个专用版本的IUserRepository。所以您需要记住哪个实体有专门的存储库,哪个实体有通用的存储库。最近我看了这篇文章,发现了一个关于“finder模式”的博客,它可能是存储库搜索的一个更好的通用解决方案:我看不出它是一个更通用的解决方案
IEntityFinder
博客文章中描述的实现仍然包含特定的方法(
ByLoginName()
ByToken