十年前我相信存在「最佳架构」,五年前我开始怀疑,现在我确信:没有最好的架构,只有最适合当前约束的架构。架构决策的本质,是在多个维度之间做权衡。
系列回顾
在这个系列中,我们走过了一段架构演进的旅程:
| 篇章 | 主题 | 核心教训 |
|---|---|---|
| 第一篇 | 混沌时代 | 没有边界的代码是灾难 |
| 第二篇 | MVC 启蒙 | 框架带来秩序,但只解决技术分层 |
| 第三篇 | Context 之道 | 从技术分层走向业务分层 |
| 第四篇 | DDD 觉醒 | 让代码说业务的语言 |
| 第五篇 | 六边形架构 | 业务逻辑不应该依赖框架 |
| 第六篇 | 模块化单体 | 单体不是问题,大泥球才是 |
| 第七篇 | 微服务 | 分布式带来新的复杂度 |
每一次演进都解决了某些问题,同时带来了新的问题。
这不是巧合,这是软件工程的本质。
复杂度守恒定律
1986 年,Fred Brooks 写了一篇著名的论文《No Silver Bullet》(没有银弹)。他提出了一个深刻的观点:
软件的本质复杂度无法被任何技术或方法消除。
Brooks 原文讨论的是:没有任何单一技术能够在十年内将软件生产力提高一个数量级。他区分了本质复杂度和偶然复杂度,认为我们只能减少偶然复杂度。
在实践中,我观察到一个相关的现象:复杂度往往从一个地方转移到另一个地方。
Brooks 区分了两种复杂度:
本质复杂度(Essential Complexity)
业务本身的复杂度。比如:
- 工资计算涉及几十种规则
- 订单状态有十几种转换
- 权限控制有复杂的继承关系
这些复杂度是业务固有的,你不能简化它,只能用代码表达它。
偶然复杂度(Accidental Complexity)
技术选择带来的复杂度。比如:
- 用 XML 配置还是注解
- 用 SQL 还是 ORM
- 用单体还是微服务
这些复杂度是我们引入的,可以通过更好的工具和方法减少。
守恒定律
我观察到一个规律:
复杂度不会消失,只会从一个地方转移到另一个地方。
| 架构选择 | 减少的复杂度 | 增加的复杂度 |
|---|---|---|
| 单体 → 微服务 | 代码耦合 | 网络通信、分布式事务 |
| 同步 → 异步 | 等待时间 | 消息顺序、重复处理 |
| SQL → NoSQL | Schema 变更 | 数据一致性、查询复杂度 |
| 自建 → 云服务 | 运维负担 | 供应商锁定、成本控制 |
每一个「简化」都在其他地方引入了新的复杂度。
架构决策的三个维度
经过这些年的实践,我总结出架构决策的三个核心维度:
维度 1:团队
团队规模
│
├── 1-5 人:能管理的复杂度有限
│ └── 推荐:单体、简单技术栈
│
├── 5-20 人:需要一定的模块化
│ └── 推荐:模块化单体、清晰的 Context
│
├── 20-100 人:需要团队自治
│ └── 推荐:微服务、独立部署
│
└── 100+ 人:需要平台化
└── 推荐:平台团队 + 业务团队
康威定律:系统设计反映组织结构。
架构应该与组织结构匹配。如果你是 5 人团队,不要搞 50 个微服务。当然,也有「逆康威策略」(Inverse Conway Maneuver)——通过调整架构来推动组织变革,但这需要管理层的支持和明确的变革目标。
维度 2:业务
业务特征
│
├── 简单 CRUD
│ └── 推荐:MVC、贫血模型、足够了
│
├── 复杂业务规则
│ └── 推荐:DDD、充血模型、事件溯源
│
├── 高并发读
│ └── 推荐:CQRS、缓存、CDN
│
├── 高并发写
│ └── 推荐:分库分表、消息队列
│
└── 实时性要求高
└── 推荐:WebSocket、边缘计算
业务决定架构,不是反过来。
维度 3:阶段
项目阶段
│
├── 0 → 1(验证想法)
│ └── 推荐:最简单的方案,快速验证
│
├── 1 → 10(找到 PMF)
│ └── 推荐:开始重构,但不要过度设计
│
├── 10 → 100(规模化)
│ └── 推荐:严肃的架构设计,考虑扩展性
│
└── 100 → ∞(成熟期)
└── 推荐:优化效率,控制技术债务
不要在 0 → 1 阶段做 10 → 100 的架构。
一个决策框架
当面临架构选择时,我会问自己这些问题:
问题 1:我们在解决什么问题?
"我们要上微服务"
→ 等等,我们在解决什么问题?
→ 部署太慢?→ 也许是 CI/CD 的问题
→ 扩展困难?→ 也许是数据库的问题
→ 团队协作难?→ 也许是代码结构的问题
不要用架构方案去找问题,要从问题出发找方案。
问题 2:这个选择的代价是什么?
"我们要用 Kubernetes"
→ 代价是什么?
→ 学习成本:团队需要几个月熟悉
→ 运维成本:需要专人维护
→ 调试成本:问题定位变复杂
值得吗?如果只有 3 台服务器,可能 Docker Compose 就够了。
问题 3:我们能承受最坏的情况吗?
"我们要用 Event Sourcing"
→ 最坏情况是什么?
→ 事件 Schema 变更很痛苦
→ 查询性能可能有问题
→ 团队不熟悉,可能踩坑
如果业务不需要审计追溯,可能不值得。
问题 4:这个决策可逆吗?
可逆决策:
- 用哪个日志库
- 用哪个测试框架
- 代码目录结构
→ 可以大胆尝试,错了再改
不可逆决策:
- 数据库选型(迁移成本高)
- 微服务拆分(拆了难合)
- 核心 API 设计(改了影响客户端)
→ 需要谨慎评估
问题 5:三年后我们会怎么想?
三年后的你,看现在的代码,会说:
A: "这个设计真有远见"
B: "当时怎么想的"
C: "虽然简单,但够用"
追求 A 和 C,避免 B。
我的架构原则
经过这些年的实践,我形成了一些个人原则:
原则 1:推迟决策
在最后责任时刻做决策。
不要在项目开始时就决定用微服务、用 Kubernetes、用 Event Sourcing。
等到真正需要的时候再决定。那时你会有更多信息。
原则 2:简单优先
能用简单方案解决的问题,不要用复杂方案。
单体能解决?→ 用单体
MVC 够用? → 用 MVC
贫血模型够用?→ 用贫血模型
复杂度是有成本的。 每增加一层抽象,就增加一点理解成本、维护成本、调试成本。
原则 3:边界清晰
不管用什么架构,模块边界必须清晰。
即使是单体,也要有清晰的 Context。即使是微服务,服务内部也要有好的结构。
边界清晰,未来才有选择。
原则 4:可演进
架构应该支持演进,而不是阻碍演进。
好的架构:
- 可以从单体平滑过渡到微服务
- 可以从同步切换到异步
- 可以从 PostgreSQL 迁移到其他数据库
坏的架构:
- 改一点就要改一大片
- 紧耦合,牵一发动全身
- 技术债务越积越多
原则 5:匹配团队
架构复杂度不应该超过团队的驾驭能力。
团队能力 = 5
架构复杂度 = 8
→ 灾难
团队能力 = 5
架构复杂度 = 4
→ 刚好
超出能力的架构,不会带来收益,只会带来混乱。
技术选型的陷阱
陷阱 1:简历驱动开发
“用 X 技术可以写在简历上”
这是最常见的陷阱。选择技术应该基于项目需求,不是个人职业发展。
陷阱 2:银弹思维
“用了 X 就能解决所有问题”
没有银弹。微服务不能解决所有问题,DDD 也不能,Kubernetes 也不能。
陷阱 3:跟风
“大厂都在用 X”
大厂的场景和你不一样。他们有几百人的团队、几亿的用户、专职的运维。
适合他们的,不一定适合你。
陷阱 4:过度设计
“将来可能会需要”
YAGNI(You Ain’t Gonna Need It)。
为「将来可能」设计的功能,80% 不会被用到。但 100% 会增加现在的复杂度。
陷阱 5:忽视人的因素
“技术上是最优解”
技术最优 ≠ 项目最优。
如果团队不熟悉,如果运维跟不上,如果业务方等不及——技术最优也是失败。
AI 时代的架构思考
2023 年之后,AI 成为新的变量。它对架构有什么影响?
新的约束条件
- Token 成本:调用 LLM 按 Token 计费,数据格式影响成本
- 推理延迟:LLM 响应时间较长,需要异步架构
- 上下文限制:LLM 有上下文窗口限制,需要合理的数据组织
新的架构模式
传统架构:
用户 → API → 业务逻辑 → 数据库
AI 增强架构:
用户 → API → AI Agent → 业务逻辑 → 数据库
│
├── 工具调用
├── 知识检索(RAG)
└── 多步推理
保持不变的原则
但核心原则不变:
- 边界清晰
- 依赖方向正确
- 复杂度可控
- 可测试、可观测
AI 是新的工具,不是新的架构哲学。
给不同阶段开发者的建议
给初学者
先学会一种架构,再学其他的
- 从 MVC 开始,理解分层
- 不要一上来就 DDD、微服务
多写代码,少看理论
- 理论只有在实践中才能理解
- 写过烂代码,才知道好代码的价值
不要怕犯错
- 犯错是学习的最好方式
- 现在犯的错,将来会成为经验
给中级开发者
开始关注「为什么」
- 不只是学怎么用,要学为什么这样设计
- 理解设计背后的权衡
尝试不同的架构风格
- 在 side project 里尝试 DDD、Event Sourcing
- 不要在生产环境冒险
学习经典
- 读《Clean Architecture》
- 读《Domain-Driven Design》
- 读《Designing Data-Intensive Applications》
给高级开发者
警惕过度设计
- 经验越多,越容易过度设计
- 保持「够用就好」的心态
关注人的因素
- 架构要服务于团队,不是反过来
- 技术决策要考虑团队能力
传承知识
- 把经验分享给团队
- 写文档、做分享、带新人
最后的话
回顾我的架构演进之路:
初期: PHP 混乱 → "代码怎么这么乱"
第二年: Django MVC → "原来可以这样分层"
后来: Phoenix Context → "原来可以按业务分"
再后来: Rust DDD → "原来领域模型可以这么丰富"
现在: → "没有银弹,只有权衡"
每一次「觉醒」,都是认知的升级。
但最大的觉醒是:架构没有对错,只有适合与否。
最好的架构,是让你忘记架构的存在。
当你不用花时间思考「这段代码放哪里」、「这个改动会影响什么」、「怎么测试这个功能」——那就是好架构。
当你每天都在和架构搏斗、和复杂度搏斗、和技术债务搏斗——那就是坏架构,不管它用了多少时髦的名词。
这个系列到此结束。感谢阅读。
如果这个系列对你有帮助,欢迎分享给你的同事和朋友。
如果你有任何问题或想法,欢迎留言讨论。
上一篇:微服务与云原生:分布式的代价
本系列:
- 混沌时代:当代码没有架构
- MVC 启蒙:框架带来的秩序
- Context 之道:从技术分层到业务分层
- DDD 觉醒:让代码说业务的语言
- 边界的艺术:六边形与洋葱
- 单体的边界:何时该拆?
- 微服务与云原生:分布式的代价
- 没有银弹:架构决策的本质(本篇)
延伸阅读: