多个实体共享同一个角色关系表
尝试遵守 DRY 原则,以下是让多个对象共享公共角色关系表的有效方法。
如果我们有以下类型(纯粹为本示例创建):
class User
{
...
}
class Article
{
...
}
这两个对象都需要针对它们定义角色。因此,Role 并不是这些对象中的任何一个所独有的。
我的想法是让存储库对这些对象执行 CRUD 操作,同时让角色拥有自己的存储库(也许可以通过服务访问)?
存储库会将 UserDTO 和 ArticleDTO 提供给 Builder 类,该类将生成必要的 Domain 对象。在这种情况下:
class User
{
...
IList<Role> Roles { get; set; }
//Other Domain objs/logic
}
class Article
{
...
IList<Role> Roles { get; set; }
//Other Domain objs/logic
}
角色对象将有一个角色表:
ID名称
和一个关系表:
itemId roleId itemType
用于将角色附加到域对象的角色构建器/服务可能看起来像这样:
static class RoleBuilder
{
IEnumerable<Role> Fetch(int id, typeof(obj))
{
//fetch from RoleRep
}
bool Save(IEnumerable<Role>, int id, typeof(obj))
{
//save through role rep
}
}
这个想法有什么本质上的错误吗? ?例如:
public static UserBuilder
{
public User FetchUser(int id)
{
//map userDTO to user
var user = map...
//populate roles
if(user != null)
user.Roles = RoleBuilder.Fetch(id, typeof(user));
}
}
另一种方法是让用户和文章管理自己的角色操作,并且可能有多个角色关系表,例如 user_has_roles、article_has_roles
第一个解决方案将允许最终用户也修改角色(重命名、添加新角色等)而不会损坏域模型,而在第二个解决方案中,我不确定如何干净地做到这一点(通过用户、通过文章更新它们?)
Trying to adhere to DRY principles, would the following be a valid way of having multiple objects share a common role relationship table.
If we have the following types (created purely for this example):
class User
{
...
}
class Article
{
...
}
Both of these objects need to have roles defined against them. As such, Role is not unique to any of these objects.
My idea was to have Repositories perform CRUD operations on these objects, but also to let Role have it's own Repository perhaps accessed via a service?
Repositories would feed out UserDTO and ArticleDTO to a Builder class which would produce the necessary Domain objects. In this case:
class User
{
...
IList<Role> Roles { get; set; }
//Other Domain objs/logic
}
class Article
{
...
IList<Role> Roles { get; set; }
//Other Domain objs/logic
}
Role object would have a Role table:
ID Name
And a relationship table:
itemId roleId itemType
the role builder/service used to attach roles to the domain object could perhaps look something like this:
static class RoleBuilder
{
IEnumerable<Role> Fetch(int id, typeof(obj))
{
//fetch from RoleRep
}
bool Save(IEnumerable<Role>, int id, typeof(obj))
{
//save through role rep
}
}
Is there anything inherently wrong with this idea? e.g:
public static UserBuilder
{
public User FetchUser(int id)
{
//map userDTO to user
var user = map...
//populate roles
if(user != null)
user.Roles = RoleBuilder.Fetch(id, typeof(user));
}
}
The alternative is to have User and Article manage their own Role manipulation and perhaps have multiple role relationship tables e.g. user_has_roles, article_has_roles
The first solution would allow roles to be modified by the end user too (renamed, new roles added etc) without corrupting the domain model whereas in the second solution, i'm not sure how to do that cleanly (update them through user, through article?)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
如果角色的概念与用户和文章是分开的——听起来确实如此,因为它适用于这两个域对象——那么我可能会像你建议的那样单独建模。
当您说“也允许最终用户修改角色”时,我不确定您的意思是什么。如果角色是一个单独的概念,那么它们可能应该有自己的域类来表示可以对角色执行的操作(重命名、删除等)。
另外,您的代码片段可能应该如下所示(在上面的代码片段中使用泛型而不是无效的 typeof() 语法):
我可能还会考虑为所有可以分配角色的域对象提供某种基类(如果角色用于安全性,然后可能是 SecurableEntity 或其他),然后您可以在数据库中拥有一个基类表来支持 ID 的共享身份(这样您就可以摆脱 Roles 表中的 itemType 列)。或者,您可以只使用 GUID 作为实体 ID,这会使很多事情变得更加容易。
If the concept of Roles is separate from User and Articles - which it sounds like it is since it applies to both of those domain objects - then I would probably model is separately like you suggest.
I'm not sure what you mean when you say "allow roles to be modified by the end user too". If roles are a separate concept they should probably have their own domain classes to represent the operations that can be performed against roles (rename, delete, etc).
Also your code snippet should probably look like this (using generics instead of the invalid typeof() syntax in your above snippet):
I would probably also consider having some sort of base class for all the domain objects which can have roles assigned (if roles are used for security then maybe SecurableEntity or something), then you can have a base class table in the DB to support a shared identities for ID's (so that you can get rid of the itemType column in the Roles table). Alternatively, you could just use GUID's for the entity ID's, which makes alot of things much easier.
你的想法听起来不错。我会避免将角色(或更一般的某些授权系统)与文章和用户模型结合起来。
我不喜欢你的代码的是静态 RoleBuilder。第一:它是静态的,这会导致耦合。其次:RoleBuilder 不应从存储库中检索任何内容,而应获取由某些服务推入的某些实例,并构建角色模型或仅从此数据中构建一个列表。如果您让 RoleBuilder 查询存储库,则您可以将 Builder 的职责与查询逻辑结合起来。
您的 UserBuilder 实现可能会出现另一个问题。对于获取单个用户可能没问题,但使用该逻辑获取多个用户将导致 Select N+1。
Your idea sounds good. I'd avoid to couple roles (or more general some authorization system) to articles and user models.
What I don't like about your code is the static RoleBuilder. First: it's static and that leads to coupling. Second: The RoleBuilder should not retrieve anything from a repository but get somes instance pushed in by some service for example and build roles model or just a list from this data. In case you let the RoleBuilder query the Repo you couple the responsibility of the Builder with the querying logic.
Another problem could arise from your UserBuilder implementation. For fetching a single user that might be ok but fetching multiple users with that logic will lead to a Select N+1.
您的多个关系表的第二个想法更合适。使用第一种方法将无法在数据库中维护干净的外键。您应该能够从任一实体更新角色,并且它将反映在下一次从另一个实体的存储库获取的内容中。在我看来,通常最好保留数据库的引用完整性,并且两个连接表都具有角色表的外键,这将阻止您执行诸如在附加到文章时从用户实体中删除角色之类的操作实体。尽管第一个解决方案很聪明,但您必须实现一些业务逻辑来确保数据完整性……当 ORM 可以为您做到这一点时,为什么还要用这种事情来扰乱您的业务层呢?
Your second idea of multiple relationship tables is more appropriate. You will not be able to maintain a clean foreign key in your database with the first method. You should be able to update the Role from either entity and it will be reflected in the next get from the repository for the other entity. In my opinion it is usually best to leave referential integrity up the database, and with both join tables having a foreign key to the roles table it will prevent you from doing things like deleting a role from the user entity when it is attached to the Article Entity. Although the first solution is clever, you will have to implement some business logic to ensure data integrity... why clutter your business layer with this kind of thing when the ORM can do it for you?