领域驱动设计(DDD)中超级用户权限的实现方案探讨
我正尝试在领域驱动设计(DDD)场景中应用安全机制,以Vernon著作中的moderatePost方法为例,该方法规定仅对应论坛的版主(Moderator)可审核该论坛内的帖子:
public void moderatePost( Post aPost, Moderator aModerator, String aSubject, String aBodyText) { this.assertStateFalse(this.isClosed(), "Forum is closed."); this.assertArgumentNotNull(aPost, "Post may not be null."); this.assertArgumentEquals(aPost.forumId(), this.forumId(), "Not a post of this forum."); this.assertArgumentTrue(this.isModeratedBy(aModerator), "Not the moderator of this forum."); aPost.alterPostContent(aSubject, aBodyText); }
问题
部分系统中存在可执行所有操作的超级用户(admin),如何在该领域模型中加入超级用户权限?我能想到的方案是重载moderatePost方法以绕过isModeratedBy断言,但担心应用层会误用该重载方法。
可行方案
1. 扩展原方法的权限校验逻辑
直接在原方法的断言中加入超级用户的判断,无需新增方法,从根源避免应用层误用:
public void moderatePost( Post aPost, Moderator aModerator, String aSubject, String aBodyText) { this.assertStateFalse(this.isClosed(), "Forum is closed."); this.assertArgumentNotNull(aPost, "Post may not be null."); this.assertArgumentEquals(aPost.forumId(), this.forumId(), "Not a post of this forum."); // 新增超级用户判断逻辑 this.assertArgumentTrue(this.isModeratedBy(aModerator) || aModerator.isSuperAdmin(), "无权限审核该帖子"); aPost.alterPostContent(aSubject, aBodyText); }
前提是Moderator模型需要添加isSuperAdmin()方法或标识属性。如果超级用户是独立于Moderator的身份模型,可以将参数调整为更通用的User类型,再判断用户是版主还是超级管理员。
2. 引入权限上下文封装判断逻辑
新增包含权限判断逻辑的AuthorizationContext对象,将权限校验从实体中解耦,同时保持实体接口的简洁性:
public void moderatePost( Post aPost, User aUser, String aSubject, String aBodyText, AuthorizationContext authContext) { this.assertStateFalse(this.isClosed(), "Forum is closed."); this.assertArgumentNotNull(aPost, "Post may not be null."); this.assertArgumentEquals(aPost.forumId(), this.forumId(), "Not a post of this forum."); // 委托权限上下文完成判断 this.assertArgumentTrue(authContext.canModerateForum(this, aUser), "无权限审核该帖子"); aPost.alterPostContent(aSubject, aBodyText); }
AuthorizationContext内部封装版主校验、超级用户校验等逻辑,领域层只依赖其提供的权限判断结果,无需关心具体规则细节。
3. 用领域服务统一处理权限
将权限校验逻辑抽离到专门的领域服务中,实体仅负责核心业务操作,避免职责过载:
public class PostModerationService { public void moderatePost(Forum forum, Post post, User user, String subject, String bodyText) { forum.assertNotClosed(); forum.assertPostBelongsToForum(post); if (!forum.isModeratedBy(user) && !user.isSuperAdmin()) { throw new IllegalArgumentException("无权限审核该帖子"); } post.alterPostContent(subject, bodyText); } }
此时可以简化Forum实体的方法,把原来的断言逻辑拆分为独立的assertNotClosed()、assertPostBelongsToForum()方法,让实体专注于自身状态校验,权限判断交由领域服务统一处理。
为什么不推荐重载方法
重载方法会增加实体接口的复杂度,应用层容易因参数匹配错误误调用超级用户专属方法,直接导致权限控制失效。同时重载不符合单一职责原则,会让实体承担过多权限判断的细节,不利于领域模型的长期维护。
内容的提问来源于stack exchange,提问作者swch

