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

78 lines
7.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# **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
* *实践:* 将外部混乱的数据模型在防腐层转换为系统内部干净的标准模型,防止外部概念“污染”我们的核心业务代码。外部接口升级时,只需修改防腐层即可。