Files
cps-develop-docs/02 - Design Standard/2.1 系统架构设计原则.md
2026-03-24 16:13:32 +08:00

7.7 KiB
Raw Blame History

2.1 系统架构设计原则

本章节规定了 CPS 研发团队在进行系统设计和架构演进时应遵循的核心指导原则。我们的目标是构建高内聚、低耦合、易于演进且对开发者友好的软件系统。

2.1.1 架构与框架边界原则

核心理念:对外统一规范,对内顺应框架(求同存异)。

很多团队在追求“跨语言/跨框架统一”时,往往会造出过度抽象的中间层,导致“对抗框架”的现象。我们主张明确界定“宏观系统架构”与“微观框架实现”的边界:

  • 系统间(宏观边界):遵循绝对统一的标准。
    • 微服务之间、前后端之间的通信,必须遵循团队统一的 API 规范(如 RESTful / gRPC、统一的鉴权机制、统一的链路追踪TraceId和日志埋点标准。
    • 系统间的交互应当是语言无关、框架无关的。
  • 系统内(微观落地):优先顺应所选框架的最佳实践。
    • 避免过度抽象: 不要为了所谓的“框架无关性”去生搬硬套。例如,在 Spring Boot 中应当充分利用其依赖注入DI和注解生态如 @Transactional在 Django 中应当拥抱其强大的 ORM 和信号机制。
    • 不强求内部绝对一致: 如果团队同时存在 Java 和 Python 栈,不要强求 Python 按照 Java 的厚重分层来写,也不要强求 Java 采用 Python 的极简脚本流。尊重并利用每种语言/框架的原生优势。

2.1.2 微服务与单体划分原则与边界

核心理念:单体优先,演进式拆分,警惕“分布式单体”。

  • 默认选择:模块化单体 (Modular Monolith)
    • 对于新业务或探索型项目,默认采用模块化单体架构。通过良好的包划分Package/Module和代码审查来保持内部逻辑的隔离而不是一上来就拆分物理微服务。
  • 微服务拆分的触发条件(何时拆分?):
    • 隔离性要求: 核心高可用业务(如交易链路)与边缘易错业务(如报表导出)需要物理隔离,防止边缘业务 OOM 拖垮核心逻辑。
    • 扩展性要求Scale 计算密集型模块(需要大量 CPU与 IO 密集型模块(需要大内存/高并发连接)需要采用不同的扩容策略。
    • 独立部署与演进: 某些模块变更极其频繁,而核心模块要求极度稳定,通过拆分实现独立发版。
    • 康威定律(团队规模): 单一代码库的协同开发人数过多,导致大量的代码冲突和发布阻塞。
  • 拆分边界与底线:
    • 禁止跨库 JOIN 微服务必须拥有独立的数据库(或独立的 Schema。如果两个服务频繁需要跨库 JOIN说明它们本该是一个服务。
    • 避免网状依赖: 服务间的调用链路应尽量保持单向或树状,严禁出现 A 调用 BB 调用 CC 又调用 A 的循环依赖。

2.1.3 分层架构与灵活决策原则

核心理念:没有银弹。架构必须匹配业务阶段与框架特性,绝不为了分层而分层。

软件分层的唯一目的是隔离变化,控制复杂度(将业务复杂度与技术实现复杂度剥离开来)。在实际落地时,团队必须基于以下基本原则,结合项目实际情况进行灵活决策并在架构决策记录ADR中予以说明

1. 分层设计的通用底线原则(必须遵守)

  • 关注点分离 (Separation of Concerns) 无论采用何种架构,必须严格区分“协议处理(如 HTTP/RPC”、“业务逻辑计算”和“数据持久化”。严禁在 Controller/View 层直接拼接 SQL也严禁在数据访问层包含核心业务判断。
  • 单向依赖流 (Unidirectional Dependency) 上层可以依赖下层(或外层依赖内层),绝对禁止下层反向依赖上层(例如:数据库持久化类绝不能引入 API 层的 request 对象或状态码)。
  • 演进式设计 (Evolutionary Design) 架构是长出来的,不是一开始就设计完美的。允许系统从简单的 CRUD 逐渐重构为复杂的领域驱动模型,前提是模块间的边界清晰。

2. 因地制宜的模式选择建议(灵活应用)

以下模式是我们的“架构工具箱”,严禁教条主义,应根据业务场景的实际痛点组合或选择使用:

  • 场景 A敏捷交付与数据驱动型业务后台管理、报表、内容发布
    • 决策建议:拥抱框架原生的 MVC / MTV 模式,追求效率。
    • 如果使用 Spring Boot可以采用经典的 Controller -> Service -> Mapper。Service 作为事务边界,处理所有逻辑。
    • 如果使用 Django / Rails应当顺应框架特色采用**“胖模型、瘦视图 (Fat Model, Skinny View)”**。利用框架内置的 ORM、信号量和 Serializer 快速打通 CRUD不要强行造一个虚伪的 Service 层。
  • 场景 B核心交易与复杂规则型业务订单流转、促销计价、资产核心
    • 决策建议:采用 领域驱动设计 (DDD) 或 干净架构/六边形架构,保护业务资产。
    • 将复杂的业务规则收拢到独立于任何框架的领域层 (Domain Layer) 中(纯粹的 POJO/POCO
    • 基础设施如数据库、Redis、MQ通过依赖倒置 (DIP) 接入。即使未来从 MySQL 换到 MongoDB或者从 Spring 换到其他框架,核心业务规则代码一行都不需要改。
  • 场景 C多端展现或前端聚合诉求强烈的业务同时支持 App、小程序、复杂 Web 端)
    • 决策建议:引入 BFF (Backend for Frontend) 层。
    • 不要让底层的核心微服务为了适配某个特定的 UI 而妥协返回臃肿的数据。
    • 在前端与核心服务之间建立 BFF 层专门负责接口聚合、字段裁剪、格式化。BFF 归属前端/客户端团队主导,底层服务归属后端团队主导。
  • 场景 D读写模型严重不平衡存在性能瓶颈秒杀扣减与复杂商品详情查询
    • 决策建议:局部或全局引入 CQRS命令与查询职责分离
    • 写操作走复杂的领域模型,保证数据一致性(写入主库)。
    • 读操作绕过所有复杂的业务对象,直接通过原生 SQL 或缓存Redis/ES构建扁平的 DTO 输出给前端,实现极致的性能。

2.1.4 依赖与解耦原则

核心理念:降低变更成本,控制爆炸半径。

  • 依赖倒置原则 (DIP - Dependency Inversion Principle)
    • 高层模块不应该依赖低层模块,两者都应该依赖其抽象(接口)。
    • 实践: 业务逻辑代码不应直接实例化具体的数据库仓储类或第三方 API 客户端而是通过构造器注入Constructor Injection接口方便后续的替换和单元测试Mock
  • 事件驱动与异步解耦:
    • 区分核心流与旁路流: 主业务流程(如“用户下单”)应当是同步的;而非核心流程(如“下单后发送短信通知”、“增加积分”)应当通过事件总线 (Event Bus)消息队列 (MQ) 异步解耦。
    • 底线: 旁路流的失败(如短信网关宕机)绝对不能导致主业务流程的回滚或失败。
  • 防腐层设计 (ACL - Anti-Corruption Layer)
    • 在对接老旧的遗留系统或不可控的第三方外部 API如支付网关、SaaS 接口必须在系统边界建立防腐层Adapter/Facade
    • 实践: 将外部混乱的数据模型在防腐层转换为系统内部干净的标准模型,防止外部概念“污染”我们的核心业务代码。外部接口升级时,只需修改防腐层即可。