You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在领域驱动设计(DDD)中适配用户、角色与权限?

如何在DDD中设计用户、角色与权限的聚合模型

针对你提到的场景(权限、角色、用户可独立存续,用户通过角色获取权限),我分享下在DDD中落地的常见设计思路——核心是让每个具备独立业务意义的实体成为自己的聚合根,用领域服务来处理跨聚合的关联逻辑:

1. 定义独立的聚合根

每个实体都有自己完整的业务生命周期,所以各自作为聚合根完全合理:

  • User聚合根:封装用户核心属性(如userId、username、email、accountStatus等),只负责用户自身的业务规则(比如修改密码、禁用账号)。内部维护assignedRoleIds集合,存储已分配的角色ID(而非Role实体本身)。
  • Role聚合根:封装角色核心属性(如roleId、roleName、description),负责角色自身的生命周期(创建、更新、标记删除)。内部维护associatedPermissionIds集合,存储关联的权限ID。
  • Permission聚合根:封装权限核心属性(如permissionId、permissionCode、resourcePath、actionType),负责权限的创建、更新等操作。

2. 用领域服务处理跨聚合的关联操作

用户分配角色、角色关联权限这类操作涉及多个聚合根的协作,不属于单一聚合根的职责,因此交给领域服务来协调:

  • UserRoleAssignmentService:
    • assignRoleToUser(userId: string, roleId: string):先加载User和Role聚合根,验证角色是否可用、用户状态是否正常,然后将roleId添加到User的assignedRoleIds集合;如果业务需要反向查询角色下的用户,也可以同时将userId添加到Role的assignedUserIds集合。
    • removeRoleFromUser(userId: string, roleId: string):同理,移除双方的关联ID。
  • RolePermissionAssociationService:
    • associatePermissionToRole(roleId: string, permissionId: string):加载Role和Permission,验证合法性后,将permissionId添加到Role的associatedPermissionIds集合。
    • disassociatePermissionFromRole(roleId: string, permissionId: string):移除关联ID。

3. 权限校验的实现逻辑

当需要判断用户是否拥有某个权限时,通过领域服务UserPermissionCheckService封装核心逻辑:

public class UserPermissionCheckService {
    private final UserRepository userRepo;
    private final RoleRepository roleRepo;
    private final PermissionRepository permissionRepo;

    // 构造函数注入依赖

    public boolean hasPermission(String userId, String permissionCode) {
        User user = userRepo.findById(userId);
        if (user == null || user.getAccountStatus() == AccountStatus.DISABLED) {
            return false;
        }

        List<Role> roles = roleRepo.findByIds(user.getAssignedRoleIds());
        Set<String> allPermissionIds = roles.stream()
                .flatMap(role -> role.getAssociatedPermissionIds().stream())
                .collect(Collectors.toSet());

        Permission targetPermission = permissionRepo.findByCode(permissionCode);
        return allPermissionIds.contains(targetPermission.getPermissionId());
    }
}

这种设计既保证了领域模型的纯净,又通过聚合根的ID关联实现了权限的传递逻辑。

关键设计原则

  • 避免跨聚合引用实体:聚合根之间只通过ID关联,不直接持有其他聚合的实体实例,这样每个聚合根可以独立修改、持久化,不会因为其他聚合的变化而受影响。
  • 单一聚合根职责:每个聚合根只维护自身的业务一致性,跨聚合的逻辑交给领域服务,符合DDD中聚合根的边界设计要求。
  • 保留独立存续能力:删除一个权限时,只需通过领域服务更新所有关联角色的associatedPermissionIds;删除一个角色时,更新用户的assignedRoleIds,完全不影响其他独立实体的存在。

内容的提问来源于stack exchange,提问作者Liarus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 06:18:30