第2章:模块边界定义

2.1 划分原则:高内聚、低耦合与上下文边界

模块边界是安全隔离的第一道防线。边界定义的核心原则包括:

  • 业务上下文完整性:同一模块应封装完整的一组业务能力,对外仅暴露必要的接口。
  • 变化隔离:将容易变化(技术栈、数据库、第三方依赖)的部分包裹在模块内部,减少变更扩散。
  • 依赖层次化:避免循环依赖,模块只能单向依赖更稳定的模块。
  • 进程级与库级差异:进程级模块(微服务)通过网络(HTTP/gRPC)通信,插件化模块通过进程内IPC或接口表通信,边界开销和信任模型不同。
经验法则:若两个功能模块需要频繁同步修改,则应合并为一个模块;若只有单向依赖且可异步,则应保持独立。

2.2 接口契约:协议、方法与版本化

接口契约是模块双方达成的通信协议,包含以下要素:

维度 说明 典型约束
通信协议 HTTP REST、gRPC、消息队列、插件接口表 必须明确序列化格式(JSON/Protobuf)
语义契约 请求/响应结构、错误码含义、幂等性保证 所有错误码需在模块文档中备案
版本策略 前置版本号、后向兼容升级、废弃周期 至少保留一个旧版本供过渡期共存
安全契约 身份令牌传递范围、加密层级、重放防护 必须原生支持端到端验证
关键约束:接口契约必须同时定义“信任假设”——调用方身份如何传递?模块是否信任上游传递的凭据?

2.3 依赖方向:稳定模块向外辐射

依赖方向决定了风险流动路径。正确划分应遵循:

  • 基础服务模块(认证、配置、日志)位于依赖底部,被上层模块引用。
  • 业务模块只能依赖基础模块,不能反向依赖或跨业务模块直接调用。
  • 插件模块对主框架是“注册-回调”关系,插件不可依赖其他插件。

依赖方向混乱会导致安全策略碎片化。例如:若支付模块依赖用户模块的本地缓存,则用户模块的缓存漏洞可直接渗透支付模块。

常见反模式:微服务间双向gRPC调用、插件A直接调用插件B的内部接口、进程级模块共享同一数据库写权限。这些设计都会打破边界信任。

2.4 边界划分实例:微服务与插件化对比

维度 微服务(进程级) 插件化(进程内)
调用方式 网络RPC(HTTP/gRPC) 方法调用(接口表)
边界隔离粒度 独立进程 / 容器 类加载器 / 沙箱
传递 HTTP Header / gRPC Metadata 调用上下文(ThreadLocal)
依赖方向 单向分层依赖 主框架→插件(单向)
安全审计点 每次RPC入口 插件注册 / 回调检查

2.5 模块边界定义总览(SVG结构图)

下图展示了一个典型的多模块系统边界划分,包含微服务A、B、C和插件模块D,箭头表示依赖方向与传递路径。

模块边界划分示意图(依赖方向与传递) 微服务A 业务模块(订单) 暴露: /createOrder 微服务B 业务模块(支付) 依赖: 认证模块 基础服务C 认证 / 配置 提供: 验证 插件D 自定义处理器 注册到主框架 (Header)传递 调用认证接口(携带) 返回身份信息 主框架调用 系统边界 图例 微服务 插件

2.6 边界定义检查清单

在架构设计阶段,逐项确认以下条目:

  1. 每个模块是否拥有唯一的业务领域(DDD限界上下文)?
  2. 模块间通信是否只有单向依赖?(允许基础模块无依赖)
  3. 接口契约是否包含安全要求(传递方式、加密范围)?
  4. 是否存在跨模块直接读写数据库或共享内存?
  5. 进程级模块的独立部署是否可验证(每个模块可单独启停)?
落地建议:使用“模块依赖图”工具(如ArchUnit、jqAssistant)自动检查循环依赖,并在CI中集成。插件化系统需强制插件遵循接口版本号,运行时拒绝不兼容的插件。