Files
cps-develop-docs/02 - Design Standard/2.1 系统架构设计原则.md

78 lines
7.7 KiB
Markdown
Raw Normal View History

2026-03-24 16:13:32 +08:00
# **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
* *实践:* 将外部混乱的数据模型在防腐层转换为系统内部干净的标准模型,防止外部概念“污染”我们的核心业务代码。外部接口升级时,只需修改防腐层即可。