我想弄清楚如何解决这个问题:
我有3张表与多对多关系。
Users *-* Roles *-* Permissions
我使用ORM从中获取数据。
我的业务层的方法必须按权限返回用户,因此我返回具有此类的对象:
public class UsersPerPermission
{
public User[] {get;set;}
public Permission {get;set;}
}
但是这个类没有映射到存储库中的任何表,它是我从现有表中生成的。这堂课应该住哪儿?
换句话说:
我应该IRepository.GetUsersPerPermission()
吗?然后该类应存在于存储库中。
或者我应该IBusinessLayer.GetUsersPerPermission()
吗?然后我必须在存储库中调用CRUD方法?
将它放在业务层中是有意义的,因为存储库应该只将CRUD操作暴露给表...但是,为了从业务层执行此操作,我将不得不执行几个独立的查询获取数据并创建“UserPerPermission”类。另一方面,如果我将它放在存储库中,我可以使用分组一次性获取该信息。
谢谢!
PS:这个中间对象的名称是什么? '转变'?
答案 0 :(得分:4)
在DDD中,大多数实体和值对象应与您ubiquitous language中已识别的域概念相对应。我通常尽可能地限制多对多关系和人工关联对象。 Eric Evans在他的书中描述了一些允许这种情况的技巧。当我必须创建一个关联对象时,它必须具有关于域的有意义的名称,基本上我永远不会命名它Class1ToClass2
。
在你的场景中,它比你的对象更加人为:
请注意,如果我们在演示文稿或应用程序层中,这种对象将毫无用处,因为它可以派生出一个包含屏幕上显示的内容(DTO)的结构。但我在这里谈论域层,它应该没有这样的复合对象。
所以我不会首先创建一个UsersPerPermission
类。如果您想要的是用户列表而用户是聚合根,只需在GetUsersByPermission()
中创建UserRepository
方法即可。这并不意味着您在应用程序服务中也不能拥有GetUsersByPermission()
方法,如果它与您的应用程序的用例匹配(显示一个用户的详细信息的屏幕)权限和拥有该权限的用户列表。)
答案 1 :(得分:0)
我同意guillaume31,不需要引入域对象“UsersPerPermission”来支持单个用例。
使用现有域类“User”,“Role”和“Permission”可以通过两种方式实现用例。
解决方案一:
假设你有:权限 - >角色 - >用户
箭头表示可导航性。权限与角色列表关联,角色与用户列表关联。
我会在Permission类中添加一个方法GetPermittedUsers() : List<User>
,这很容易实现。
UI逻辑将调用PermissionRepository的GetPermissions(),然后在每个Permission上调用GetPermittedUsers()。
我假设您使用像hibernate(Nhibernate)这样的ORM框架并正确定义多对多关系。如果您从权限定义角色和用户的预先加载,ORM将生成一个查询,该查询将权限,角色和用户表连接在一起并一次性加载所有内容。如果为Role和User定义延迟加载,则在调用PermissionRepository时将在一个查询中加载权限列表,然后在另一个查询中加载所有关联的角色和用户。一切都是从数据库加载,最多三个查询。这被称为1 + n问题,大多数ORM都能正确处理。
解决方案二:
假设你有:用户 - &gt;角色 - &gt;许可
箭头表示可导航性。用户有一个角色列表。角色有一个权限列表。
我将getUsersByPermissions(List<long> permissionIds) : List<Users>
添加到UserRepository,并将getPermissions() : List<Permission>
添加到User类。
UserRepository的实现需要在单个查询中将用户,角色和权限表连接在一起,并一次性加载所有内容。同样,大多数ORM都会正确处理它。
获得用户列表后,您可以非常轻松地创建一种方法来构建Map<Permission, List<User>>
。
说实话,我很喜欢解决方案。我避免编写一个复杂的方法来将用户列表转换为权限和用户的映射,因此我不需要担心放置此方法的位置。但是,如果您已经在另一个方向上具有导航性,则解决方案可以在用户,角色和权限类之间创建循环关系。有些人不喜欢循环关系。我认为如果用户需要它,有时候循环关系是可以接受的。
答案 2 :(得分:0)
在类似的上下文中,我在域服务中使用了一种返回类似
的查询方法IEnumerable<KeyValuePair<PermissionName, IEnumerable<Username>>>
使用KeyValuePair&lt;&gt;我避免使用人工概念(如UsersPerPermition)污染域模型。而且这种结构是不可改变的。 我没有在存储库上使用查询方法,因为在我的上下文中,没有实体与另一个实体耦合。因此,任何存储库都无关紧要。
但是,此解决方案对您的GUI非常有用,当且仅当您正确建模了实体的标识符时(在您的示例中,权限和用户都是实体)。 事实上,如果它们shared identifiers属于用户所理解的无处不在的语言,那么它们就足够了,无需进一步描述。
否则,您只是为GUI构建一个有用的DTO。它不属于域,因此您应该使用最简单的可行方法(ADO.NET查询?更简单的东西?)。 实际上,在我自己的场景中,GUI和域都使用了这样的服务(GUI显示了详细的预览)。
通常,域模型必须反映域专家的语言,捕获与有界上下文相关的知识。其他所有内容都必须在域外(但大部分时间都可以用域的值对象表示)。