DDD 存储库输入参数

DDD repository input parameters

在以下示例中,建议采用哪种方式来实施 _customRoleRepository? 以下代码在应用服务中执行。

var user = _userRepository.GetById(1);
var customRole = _customRoleRepository.GetById(user.CustomRoleId);

var user = _userRepository.GetById(1);
var customRole = _customRoleRepository.GetForUser(user);

不想这么说,但在 DDD 环境中,我的回答是两者都不是。

在您的第一个示例中,角色存储库可以不知道用户域,这很好,但这意味着应用程序需要知道要获得角色,它需要从用户中提取一个 ID,然后查询另一个存储库。换句话说,应用程序充当用户和角色之间的映射器。

在第二个示例中,角色存储库现在需要了解用户域。不太好,但另一方面,应用程序不再需要了解 roleId。所以这很好。两种方法之间的经典权衡。

但是在这两种情况下,应用程序仍然需要两个存储库来获取它的信息。当需要更多关系时会发生什么?存储库的数量会迅速增长,事情会变得一团糟。

在领域驱动设计中,您应该尝试从聚合根 (AR) 和领域上下文的角度来思考。对于您的示例上下文,用户是 AR,角色变为 child。所以你可能有:

var user = _userFinder.GetById(1);
var customRole = user.CustomRole;

这对您的应用程序隐藏了大部分实施细节,让您可以专注于领域实体实际需要做的事情。

考虑到这两个选项,我可能会选择第一个,它可以保持通过 ID 访问的一致性。

如果可能,最好在加载用户时加载自定义角色,以避免再次往返数据库,尤其是在这是常见操作的情况下。这可以实现为读取模型。

这是假设您已经正确地为聚合建模...:)

两者都同样有效,具体取决于您的需要。如果您想在尝试检索角色之前确保调用代码具有有效的用户聚合,GetForUser 会很好 - 虽然它确实将 customRoleRepository 与用户聚合的知识相结合,如果您想要调用代码拥有一个有效的用户聚合,那么这种耦合就有了目的。

GetByUserIdGetById 更一致并且耦合度较低,因此如果在您的上下文中调用 GetByUserId 并不重要,即使客户端没有有效的用户聚合,那也可以。

如果您正在加载 ById,我还发现使用类型化标识值对象对于提供额外级别的类型安全非常有帮助 - 这里有一些关于优缺点的讨论 and here https://groups.google.com/forum/#!topic/dddcqrs/WQ9zRtW3Gbg