十年前我相信存在「最佳架构」,五年前我开始怀疑,现在我确信:没有最好的架构,只有最适合当前约束的架构。架构决策的本质,是在多个维度之间做权衡。

系列回顾

在这个系列中,我们走过了一段架构演进的旅程:

篇章主题核心教训
第一篇混沌时代没有边界的代码是灾难
第二篇MVC 启蒙框架带来秩序,但只解决技术分层
第三篇Context 之道从技术分层走向业务分层
第四篇DDD 觉醒让代码说业务的语言
第五篇六边形架构业务逻辑不应该依赖框架
第六篇模块化单体单体不是问题,大泥球才是
第七篇微服务分布式带来新的复杂度

每一次演进都解决了某些问题,同时带来了新的问题。

这不是巧合,这是软件工程的本质。

复杂度守恒定律

1986 年,Fred Brooks 写了一篇著名的论文《No Silver Bullet》(没有银弹)。他提出了一个深刻的观点:

软件的本质复杂度无法被任何技术或方法消除。

Brooks 原文讨论的是:没有任何单一技术能够在十年内将软件生产力提高一个数量级。他区分了本质复杂度和偶然复杂度,认为我们只能减少偶然复杂度。

在实践中,我观察到一个相关的现象:复杂度往往从一个地方转移到另一个地方。

Brooks 区分了两种复杂度:

本质复杂度(Essential Complexity)

业务本身的复杂度。比如:

  • 工资计算涉及几十种规则
  • 订单状态有十几种转换
  • 权限控制有复杂的继承关系

这些复杂度是业务固有的,你不能简化它,只能用代码表达它。

偶然复杂度(Accidental Complexity)

技术选择带来的复杂度。比如:

  • 用 XML 配置还是注解
  • 用 SQL 还是 ORM
  • 用单体还是微服务

这些复杂度是我们引入的,可以通过更好的工具和方法减少。

守恒定律

我观察到一个规律:

复杂度不会消失,只会从一个地方转移到另一个地方。

架构选择减少的复杂度增加的复杂度
单体 → 微服务代码耦合网络通信、分布式事务
同步 → 异步等待时间消息顺序、重复处理
SQL → NoSQLSchema 变更数据一致性、查询复杂度
自建 → 云服务运维负担供应商锁定、成本控制

每一个「简化」都在其他地方引入了新的复杂度。

架构决策的三个维度

经过这些年的实践,我总结出架构决策的三个核心维度:

维度 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 是新的工具,不是新的架构哲学。

给不同阶段开发者的建议

给初学者

  1. 先学会一种架构,再学其他的

    • 从 MVC 开始,理解分层
    • 不要一上来就 DDD、微服务
  2. 多写代码,少看理论

    • 理论只有在实践中才能理解
    • 写过烂代码,才知道好代码的价值
  3. 不要怕犯错

    • 犯错是学习的最好方式
    • 现在犯的错,将来会成为经验

给中级开发者

  1. 开始关注「为什么」

    • 不只是学怎么用,要学为什么这样设计
    • 理解设计背后的权衡
  2. 尝试不同的架构风格

    • 在 side project 里尝试 DDD、Event Sourcing
    • 不要在生产环境冒险
  3. 学习经典

    • 读《Clean Architecture》
    • 读《Domain-Driven Design》
    • 读《Designing Data-Intensive Applications》

给高级开发者

  1. 警惕过度设计

    • 经验越多,越容易过度设计
    • 保持「够用就好」的心态
  2. 关注人的因素

    • 架构要服务于团队,不是反过来
    • 技术决策要考虑团队能力
  3. 传承知识

    • 把经验分享给团队
    • 写文档、做分享、带新人

最后的话

回顾我的架构演进之路:

初期: PHP 混乱 → "代码怎么这么乱"
第二年: Django MVC → "原来可以这样分层"
后来: Phoenix Context → "原来可以按业务分"
再后来: Rust DDD → "原来领域模型可以这么丰富"
现在: → "没有银弹,只有权衡"

每一次「觉醒」,都是认知的升级。

但最大的觉醒是:架构没有对错,只有适合与否。

最好的架构,是让你忘记架构的存在。

当你不用花时间思考「这段代码放哪里」、「这个改动会影响什么」、「怎么测试这个功能」——那就是好架构。

当你每天都在和架构搏斗、和复杂度搏斗、和技术债务搏斗——那就是坏架构,不管它用了多少时髦的名词。


这个系列到此结束。感谢阅读。

如果这个系列对你有帮助,欢迎分享给你的同事和朋友。

如果你有任何问题或想法,欢迎留言讨论。


上一篇:微服务与云原生:分布式的代价

本系列:

  1. 混沌时代:当代码没有架构
  2. MVC 启蒙:框架带来的秩序
  3. Context 之道:从技术分层到业务分层
  4. DDD 觉醒:让代码说业务的语言
  5. 边界的艺术:六边形与洋葱
  6. 单体的边界:何时该拆?
  7. 微服务与云原生:分布式的代价
  8. 没有银弹:架构决策的本质(本篇)

延伸阅读: