权限继承设计:Modular Monolith DDD角色与用户组实战

权限继承设计:Modular Monolith DDD角色与用户组实战

【免费下载链接】modular-monolith-with-ddd Full Modular Monolith application with Domain-Driven Design approach. 【免费下载链接】modular-monolith-with-ddd 项目地址: https://gitcode.com/GitHub_Trending/mo/modular-monolith-with-ddd

引言:权限管理的痛点与DDD解决方案

在复杂业务系统中,权限管理往往面临三大挑战:角色定义混乱权限分配繁琐跨模块权限协同困难。尤其在模块化单体(Modular Monolith)架构中,如何在保持模块内聚性的同时实现灵活的权限继承,成为Domain-Driven Design(领域驱动设计)实践中的关键课题。

本文基于真实Modular Monolith项目(modular-monolith-with-ddd),从领域模型设计、数据库架构到查询实现,全面解析角色与权限继承的实战方案。读完本文你将掌握:

  • DDD值对象(Value Object)模式在角色定义中的应用
  • 基于数据库视图的权限聚合查询实现
  • 模块化架构下的权限隔离与跨模块协同策略
  • 角色继承与用户组权限的设计模式

一、领域模型设计:角色与权限的DDD表达

1.1 角色定义:值对象模式的应用

在UserAccess模块的领域层中,UserRole类采用值对象(Value Object)模式实现,确保角色定义的不可变性和业务语义的完整性:

// src/Modules/UserAccess/Domain/Users/UserRole.cs
public class UserRole : ValueObject
{
    public static UserRole Member => new UserRole(nameof(Member));
    public static UserRole Administrator => new UserRole(nameof(Administrator));

    public string Value { get; }

    private UserRole(string value)
    {
        this.Value = value;
    }
}

设计亮点

  • 通过静态属性定义系统内置角色,避免魔法字符串
  • 私有构造函数确保角色实例只能通过预定义常量创建
  • 继承ValueObject基类,重写相等性比较逻辑(由BuildingBlocks提供支持)

1.2 用户-角色关联:聚合根中的权限载体

User聚合根通过_roles字段维护用户与角色的多对多关系,在用户创建时分配初始角色:

// src/Modules/UserAccess/Domain/Users/User.cs
public class User : Entity, IAggregateRoot
{
    private List<UserRole> _roles;

    private User(
        Guid id,
        string login,
        string password,
        string email,
        string firstName,
        string lastName,
        string name,
        UserRole role)
    {
        // 基础属性初始化...
        _roles = [role];  // 初始角色分配
        this.AddDomainEvent(new UserCreatedDomainEvent(this.Id));
    }

    public static User CreateAdmin(...) { /* 分配Administrator角色 */ }
    public static User CreateUser(...) { /* 分配Member角色 */ }
}

领域事件:用户创建时发布UserCreatedDomainEvent,可用于跨模块权限初始化(如自动分配默认权限集)。

二、数据库架构:权限继承的持久化设计

2.1 权限关联表结构

InitializeDatabase.sql定义了权限系统的核心表结构,采用经典的RBAC(基于角色的访问控制)模型:

-- 用户-角色关联表
CREATE TABLE [users].[UserRoles] (
    [UserId]   UNIQUEIDENTIFIER NOT NULL,
    [RoleCode] NVARCHAR (50)    NULL
);

-- 角色-权限关联表
CREATE TABLE [users].[RolesToPermissions] (
    [RoleCode]       VARCHAR (50) NOT NULL,
    [PermissionCode] VARCHAR (50) NOT NULL,
    CONSTRAINT [PK_RolesToPermissions_RoleCode_PermissionCode] PRIMARY KEY CLUSTERED ([RoleCode] ASC, [PermissionCode] ASC)
);

-- 权限定义表
CREATE TABLE [users].[Permissions] (
    [Code]        VARCHAR (50)  NOT NULL,
    [Name]        VARCHAR (100) NOT NULL,
    [Description] VARCHAR (255) NULL,
    CONSTRAINT [PK_users_Permissions_Code] PRIMARY KEY CLUSTERED ([Code] ASC)
);

2.2 权限继承视图设计

通过视图v_UserPermissions实现角色权限的自动聚合,简化权限查询逻辑:

CREATE VIEW [users].[v_UserPermissions]
AS
SELECT 
    DISTINCT
    [UserRole].UserId,
    [RolesToPermission].PermissionCode
FROM [users].UserRoles AS [UserRole]
    INNER JOIN [users].RolesToPermissions AS [RolesToPermission]
        ON [UserRole].RoleCode = [RolesToPermission].RoleCode

视图作用

  • 隐藏多表关联复杂性,提供用户权限的扁平化查询
  • 支持权限继承:当角色权限更新时,用户权限自动生效
  • 为跨模块权限查询提供统一数据接口

三、权限查询实现:从数据库到应用服务

3.1 权限查询处理程序

GetUserPermissionsQueryHandler通过查询v_UserPermissions视图获取用户权限列表:

// src/Modules/UserAccess/Application/Authorization/GetUserPermissions/GetUserPermissionsQueryHandler.cs
internal class GetUserPermissionsQueryHandler : IQueryHandler<GetUserPermissionsQuery, List<UserPermissionDto>>
{
    private readonly ISqlConnectionFactory _sqlConnectionFactory;

    public async Task<List<UserPermissionDto>> Handle(GetUserPermissionsQuery request, CancellationToken cancellationToken)
    {
        var connection = _sqlConnectionFactory.GetOpenConnection();
        const string sql = $"""
            SELECT [UserPermission].[PermissionCode] AS [{nameof(UserPermissionDto.Code)}]
            FROM [users].[v_UserPermissions] AS [UserPermission] 
            WHERE [UserPermission].UserId = @UserId
        """;
        var permissions = await connection.QueryAsync<UserPermissionDto>(sql, new { request.UserId });
        return permissions.AsList();
    }
}

设计特点

  • 依赖ISqlConnectionFactory抽象,支持多数据库实现
  • 通过Dapper执行原生SQL,直接查询优化后的视图
  • 返回DTO对象,实现领域模型与API契约的解耦

3.2 权限继承的隐式实现

虽然代码中未直接定义角色层次结构,但通过以下方式支持权限继承:

  1. 角色组合:用户可同时拥有多个角色(如Member + Moderator),权限自动合并
  2. 权限粒度控制:通过细粒度权限定义(如Meeting.CreateMeeting.Edit)和角色映射实现灵活授权
  3. 跨模块权限隔离:权限代码可按模块前缀划分(如Administration.*Meetings.*

四、扩展设计:用户组与动态权限管理

4.1 用户组模拟实现方案

尽管当前代码未直接提供用户组(Group)实体,但可通过以下方式模拟用户组功能:

// 扩展User类,添加角色管理方法
public class User : Entity, IAggregateRoot
{
    // ...现有代码...
    
    public void AddRole(UserRole role)
    {
        if (!_roles.Contains(role))
        {
            _roles.Add(role);
            // 发布角色变更事件
            this.AddDomainEvent(new UserRoleAddedDomainEvent(this.Id, role));
        }
    }
}

用户组替代方案

  • 创建"虚拟角色"(如MarketingTeam),将用户添加到该角色实现组权限
  • RolesToPermissions表中为虚拟角色分配特定权限集
  • 通过领域服务UserGroupService管理用户-虚拟角色关联

4.2 权限继承增强建议

基于项目现有架构,可通过以下方式增强权限继承能力:

mermaid

增强方向

  1. 显式角色层次:添加ParentRoleCode字段到Roles表,实现角色继承
  2. 条件权限:扩展RolesToPermissions表,添加Condition字段支持基于上下文的权限判断
  3. 权限有效期:添加ValidFromValidTo字段,支持临时权限分配

五、最佳实践与常见陷阱

5.1 DDD权限设计最佳实践

  1. 权限作为领域概念:核心权限逻辑(如"只有管理员可删除会议")应作为领域规则实现
// 领域规则示例(Meetings模块)
public class Meeting : Entity
{
    public void Delete(User deletingUser)
    {
        if (!deletingUser.HasPermission(PermissionCodes.Meeting.Delete))
        {
            throw new BusinessRuleValidationException("Insufficient permissions to delete meeting");
        }
        // 其他删除逻辑...
    }
}
  1. 权限检查集中化:通过装饰器模式统一权限验证
public class PermissionCommandHandlerDecorator<TCommand> : ICommandHandler<TCommand>
{
    private readonly ICommandHandler<TCommand> _decorated;
    private readonly IPermissionChecker _permissionChecker;

    public async Task Handle(TCommand command, CancellationToken cancellationToken)
    {
        var requiredPermission = GetPermissionForCommand(typeof(TCommand));
        if (!await _permissionChecker.HasPermission(requiredPermission))
        {
            throw new AccessDeniedException();
        }
        await _decorated.Handle(command, cancellationToken);
    }
}

5.2 常见陷阱与解决方案

问题解决方案
权限检查散布在UI层使用命令/查询装饰器统一拦截
权限粒度粗导致授权不灵活细化权限定义(如Meeting.Create vs Meeting.Edit
角色爆炸难以维护引入权限组和角色继承
跨模块权限协同复杂通过集成事件同步权限变更

六、总结与扩展方向

本文深入分析了Modular Monolith项目中基于DDD的权限继承设计,核心要点包括:

  1. 值对象角色定义:确保角色语义明确且不可变
  2. RBAC数据库模型:通过角色-权限关联实现基础权限继承
  3. 视图驱动查询:简化权限聚合查询,提升性能
  4. 领域事件驱动:支持权限变更的跨模块同步

未来扩展方向

  • 实现角色层次结构(如SuperAdmin继承Admin权限)
  • 添加用户组实体,支持批量权限管理
  • 引入属性基础访问控制(ABAC),基于上下文动态授权
  • 开发权限管理UI,支持动态角色-权限配置

通过本文介绍的设计模式和实现方式,你可以在模块化单体架构中构建既灵活又安全的权限系统,为未来向微服务架构演进奠定基础。

【免费下载链接】modular-monolith-with-ddd Full Modular Monolith application with Domain-Driven Design approach. 【免费下载链接】modular-monolith-with-ddd 项目地址: https://gitcode.com/GitHub_Trending/mo/modular-monolith-with-ddd

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值