第3章 SDK架构设计:分层、模块与适配器
SDK 的架构设计,说到底就是两件事:分清楚责任,和留好扩展口。RBAC 模型嵌进来以后,这两件事会变得特别棘手。为什么?因为权限判断会渗透到 SDK 的每一层——你总不能在每个函数里都写一遍「if 用户有角色 then 放行 else 拒绝」吧?嗯,我见过有人这么干过,结果代码像蜘蛛网,改一个接口要动七八个地方。
这一章我们就把 SDK 的骨架搭起来。我会把 分层设计、模块划分、适配器模式 这三个东西串在一起讲。你在项目中遇到的问题,八成都能在这三个概念里找到解法。
3.1 分层设计:别让权限逻辑到处乱窜
我个人习惯把 SDK 分成四层。你想想看,一个 SDK 最终要给业务方调用,业务方只管「我要查用户的权限」,他根本不应该关心你是从本地缓存取的,还是从远程 API 拉的,更不该关心你的 RBAC 规则引擎怎么运行的。
| 层名称 | 职责 | 与 RBAC 的关系 |
|---|---|---|
| 接口层(API Layer) | 对外暴露简洁的 API | 提供 checkPermission、getRoles、assignRole 等接口 |
| 核心业务层(Core Layer) | 实现 RBAC 模型逻辑 | 角色继承、权限树、会话管理 |
| 数据访问层(Data Layer) | 抽象数据存储方式 | 屏蔽 MySQL、Redis、本地 JSON 文件等差异 |
| 适配层(Adapter Layer) | 对接外部服务与框架 | 让 SDK 能在 Spring、Express、Serverless 等环境中运行 |
我在项目中遇到过最典型的反例:有人把权限缓存逻辑写在接口层,结果每次换缓存中间件都要改 SDK 的公共 API 签名。说白了,这层混在一起,改一处崩一片。
3.2 模块划分:每个模块只干一件事
分层定好了垂直边界,模块划分定的是水平边界。我建议至少拆成下面 6 个模块:
- 认证模块(Auth) —— 身份令牌的签发、验证、刷新。没有身份,谈什么权限?
- 角色管理模块(Role) —— 角色 CRUD、角色树、角色继承。RBAC 的核心之一。
- 权限管理模块(Permission) —— 资源与操作的权限定义,以及权限与角色的绑定。
- 会话模块(Session) —— 用户上下文在请求链中的传递。权限判断需要知道「当前是谁」。
- 策略引擎模块(Engine) —— 决策逻辑,比如「是否允许用户 A 对资源 X 执行操作 Y」。
- 存储抽象模块(Store) —— 上面提到的数据访问层,对外提供一致的读写接口。
3.3 适配器模式:让 SDK 拥抱变化
为什么要提适配器模式?因为 SDK 永远不知道会被扔到什么环境里。我曾经接过一个需求:把我们的 RBAC SDK 嵌入到一个 IoT 网关设备上,那设备跑的是轻量级 Linux,连标准 HTTP 库都没有。如果 SDK 代码里到处都是 hardcode 的 HttpClient 调用,那这个需求就彻底没戏了。
适配器模式在这里的作用是:定义接口,让使用者提供实现。SDK 内部只依赖这些接口,不关心具体实现。
下面是一个简化示例——我们把「持久化存储」抽象成接口:
// 定义适配器接口
public interface PermissionStore {
List<Role> getRolesByUserId(String userId);
List<Permission> getPermissionsByRoleId(String roleId);
void saveRoleAssignment(String userId, String roleId);
}
// SDK 内部只依赖接口
public class RbacEngine {
private final PermissionStore store;
public boolean checkPermission(String userId, String resource, String action) {
List<Role> roles = store.getRolesByUserId(userId);
// ... 业务逻辑
return true;
}
}
// 使用者提供具体实现
public class MySqlPermissionStore implements PermissionStore {
// 实现具体 SQL 查询
}
除了存储适配器,你还可能需要:
- 缓存适配器 —— 让使用者选本地 Map、Caffeine 还是 Redis。
- 日志适配器 —— 对接使用者的日志框架(SLF4J、log4j、甚至直接 System.out)。
- 上下文适配器 —— 从 HTTP Header、gRPC Metadata 或消息队列中提取当前用户身份。
3.4 架构总览:一张图串起来
上面说的这些东西,我画成了一张结构图。你一眼就能看清层次和模块关系:
你看这张图,从上到下分别是接口层、核心业务层、适配层、数据访问层。中间的核心业务层又拆成了 6 个模块。适配层像个「转换器」,把 SDK 内部接口映射到使用者的基础设施上。
3.5 避坑指南:我记了三年的教训
最后分享一个我当年犯过的错误。那时我刚做 SDK 开发,认为「分层」就是多写几个 package 完事。结果适配器接口放在了核心层里,导致核心业务代码不得不 import 具体的存储实现类。每次改存储层,核心层也要重新编译。后来我花了整整两天重构才把依赖调回来。
规则很简单:核心层只依赖接口定义,接口定义放在一个单独的「契约」包(比如 com.xxx.rbac.contract)里。适配器实现和具体存储实现都在上层或外部模块里。这样你换任何一个实现,核心层一行代码都不用改。