名称:系统架构设计 描述:在构建阶段指导清洁、可扩展的系统架构。当设计模块、定义边界、结构化项目、管理依赖或防止系统增长时的紧耦合和脆弱性时使用。
系统架构设计
核心原则: 早期的小决策会累积成清洁的系统或巨大的技术债务。结构正确,系统在增长时保持可维护性;结构错误,每次变更都会变得更难。
主题选择
根据您正在处理的内容加载相关参考:
| 处理中… | 加载 | 文件 |
|---|---|---|
| 层、垂直切片、文件组织 | 结构 | references/structure.md |
| 接口、依赖反转、合同 | 耦合 | references/coupling.md |
| 有界上下文、API设计、模块边界 | 边界 | references/boundaries.md |
| 异步模式、竞争条件、队列 | 并发 | references/concurrency.md |
| 日志记录、健康检查、指标、追踪 | 可观察性 | references/observability.md |
当任务跨越多个主题时,加载多个参考。对于大多数新项目,从结构和耦合开始。
核心原则(所有主题)
惯例优于创新
默认使用已建立的模式、标准库和经过验证的惯例。新解决方案带来隐藏成本:文档负担、入门摩擦和维护意外。
| 偏好 | 而非 |
|---|---|
| 框架惯例 | 自定义项目结构 |
| 标准库工具 | 针对已解决问题定制的实用程序 |
| 已建立的模式(MVC、仓库等) | 聪明的抽象 |
| 有效的无聊技术 | 可能有效的激动技术 |
测试: 如果有新人加入团队,他们能多快找到东西并理解结构?如果答案需要导游,那么惯例不够强。
状态管理
状态是复杂性隐藏的地方。状态存在的地方越多,能改变它的东西越多,系统就越难推理。
- 最小化可变共享状态。 如果两个模块需要相同数据,一个应拥有它,另一个应请求它。
- 保持状态接近使用位置。 全局状态几乎从来不是答案。
- 使状态变化显式。 无论是通过事件、还原器还是显式的设置器方法,“什么改变了以及为什么”应可追溯。
- 单一来源真相。 每个数据都应有唯一的权威来源。派生数据应计算得出,而非独立存储,除非有测量的性能原因需要缓存或反规范化。
为变化设计
- 使常见路径容易。 如果做正确的事需要额外努力,人们会走捷径。良好的默认值、模板和护栏胜过文档。
- 用工具强制,而非文档。 代码检查规则、CI检查和架构测试可扩展。维基页面和团队协议不行。如果一个惯例重要,使违反惯例导致构建失败。
- 隔离波动性。 用适配器包装外部集成。在领域层隔离业务规则。保持表示层薄。在仓库后抽象存储。
- 偏好组合而非继承。 组合小而专注的片段,而非扩展复杂的基类。
复杂度预算
每个架构决策都有复杂度成本。在重要处花费这个预算。
| 值得复杂度 | 不值得 |
|---|---|
| 独立变化的领域之间的分离 | 抽象只有一种实现的代码 |
| 真正异步工作流的事件驱动 | 简单请求-响应流的事件驱动 |
| 测量性能瓶颈的缓存 | “以防万一”的缓存 |
| 独立部署团队的微服务 | 小团队单体的微服务 |
规则: 不需要时不添加间接层。过早抽象与过早优化一样代价高昂。两个相似的代码块比错误的抽象更好。
快速参考
| 问题 | 响应 |
|---|---|
| “这段代码放哪里?” | 如果答案不明显,结构需要改进 |
| “改变X需要接触Y” | X和Y之间缺少边界 |
| “这个模块做太多事” | 沿独立的变更原因拆分 |
| “我们无法单独测试这个” | 隐藏依赖;改为注入它们 |
| “新开发者需要几周才能高效” | 惯例太弱或太新颖 |
| “每个PR触及10个文件” | 功能代码分散;共置它 |
| “共享文件夹不断增长” | 边界位置错误 |
写架构文档? 此技能涵盖如何构建。关于如何写(ADR、设计文档、权衡分析),加载Skill(ce:writer)并使用建筑师角色。