关于本书:《Software Architecture: The Hard Parts》(软件架构难点解惑)由 Neal Ford、Mark Richards、Pramod Sadalage 和 Zhamak Dehghani 四位软件架构领域的顶级专家共同撰写,于 2021 年由 O'Reilly 出版。本书聚焦现代软件架构中没有标准答案的难点问题——那些需要架构师在多种方案之间做出艰难权衡的决策。
传统软件架构书籍倾向于给出"最佳实践"或"模式",而本书倡导截然不同的理念:所有架构决策都是权衡(trade-off)。没有银弹,没有放之四海而皆准的"正确选择",只有在特定上下文中最合理的平衡点。
本书的核心方法论是权衡分析(Trade-Off Analysis),它指导架构师在分布式系统的各个维度间做出理性决策。
传统软件开发强调"找到最佳方案",但软件架构的残酷现实是:每个选择都意味着放弃某些东西。
| 决策维度 | 选择A的优势 | 选择A的代价 |
|---|---|---|
| 服务粒度 | 细粒度:独立性强 | 分布式复杂性上升 |
| 数据架构 | 数据库共享:一致性高 | 耦合度增加 |
| 通信方式 | 异步:解耦程度高 | 调试和排错困难 |
本书提出的权衡分析框架包括以下核心步骤:
Hugo 的经验:在实际项目中,我见过太多团队跳过前两步直接跳到方案选择,结果选了"最新潮"的技术方案而非最适合的方案。权衡分析的真正价值不在打分表上,而在讨论过程中迫使团队直面取舍。
单体架构在项目初期是合理的——快速交付、部署简单、调试容易。但随着系统演进:
遵循**领域驱动设计(DDD)**的限界上下文(Bounded Context)原则:
单体系统
├── 用户管理(Bounded Context) → 用户服务
├── 订单处理(Bounded Context) → 订单服务
├── 支付结算(Bounded Context) → 支付服务
├── 库存管理(Bounded Context) → 库存服务
└── 物流配送(Bounded Context) → 物流服务
根据 Eric Evans 的 DDD 方法论,将系统按核心域(Core Domain)、支撑域(Supporting Domain)、通用域(Generic Domain)分解:
| 子域类别 | 策略 | 服务优先级 |
|---|---|---|
| 核心域 | 内部自建,投入最大资源 | P0 |
| 支撑域 | 内部简化实现 | P1 |
| 通用域 | 使用第三方 SaaS 或开源方案 | P2 |
Hugo 的踩坑记录:一次项目中,团队选择按"功能模块"而非"限界上下文"分解,结果两个服务之间出现了大量的双向调用和数据冗余。正确做法是先厘清业务边界,识别聚合根,再确定服务边界。
不应一次性拆分解单体,而应采用绞杀者模式(Strangler Fig Pattern):
第一阶段:在单体前架设路由代理
第二阶段:逐个将功能模块剥离为新服务
第三阶段:路由代理将新服务的请求转发到独立服务
第四阶段:完全拆解单体
Hugo 的实践建议:推荐从"低耦合、高独立"的模块开始剥离,比如报表模块、通知模块、用户认证模块。这些模块与核心业务逻辑的关联较弱,剥离风险最小。先做快速成功案例建立团队信心,再啃硬骨头。
单体架构中的 ACID 事务在分布式环境下不再适用,数据的分布在各个服务间造成了天然的矛盾。
适用场景:迁移初期,或两个服务间存在强数据依赖。
优点:
缺点:
用户服务 → 用户数据库(私有)
订单服务 → 订单数据库(私有)
支付服务 → 支付数据库(私有)
优点:
缺点:
适合场景:需要完整审计日志、复杂事件回溯的高合规场景。
Hugo 的项目案例:在某个金融交易系统中,我们采用了数据库按服务私有化的策略,但在订单服务与支付服务之间保留了共享的"交易对账表"。这个折中方案意味着我们需要在服务边界上接受一定程度的数据耦合,但这是值得的权衡——它减少了对账逻辑的复杂度,同时不影响其他服务的独立性。
关键原则:谁拥有数据,谁负责维护数据的一致性。
数据所有权规则:
在单体架构中,ACID 事务保证了数据的一致性。但在分布式环境下:
Saga 是一种补偿型事务模式,将一个大事务拆分为一系列本地事务,每个事务执行后发布事件触发下一个事务。
服务A执行 → 发布事件 →
服务B监听并执行 → 发布事件 →
服务C监听并执行 → 完成
缺点:业务逻辑分散在多个服务中
协调器
├── 调用服务A → 完成
├── 调用服务B → 完成
├── 调用服务C → 完成
└── 所有完成 → 整个事务成功
优点:事务逻辑集中在协调器,便于管理
关键概念:每个 Saga 步骤必须提供对应的补偿动作:
| 步骤 | 正常动作 | 补偿动作 |
|---|---|---|
| 创建订单 | 插入订单记录 | 标记订单为"已取消" |
| 扣减库存 | 减少库存计数 | 恢复库存计数 |
| 处理支付 | 扣除金额 | 发起退款 |
| 安排发货 | 创建发货单 | 取消发货单 |
Hugo 的踩坑记录:第一次实现 Saga 时,我们低估了补偿的复杂性。补偿不是简单的"回滚"——已发送的邮件不能撤回,已发货的包裹需要逆向物流。需要针对每种补偿场景设计 idempotent 的补偿逻辑。
分布式环境下的最佳实践是接受最终一致性,而非追求强一致性:
| 模式 | 同步/异步 | 耦合度 | 可靠性 | 适用场景 |
|---|---|---|---|---|
| REST/HTTP | 同步 | 中 | 低(依赖网络) | 查询类操作 |
| gRPC | 同步 | 中 | 中 | 高性能服务间调用 |
| 消息队列 | 异步 | 低 | 高 | 事件驱动、异步处理 |
| 事件流 | 异步 | 极低 | 高 | CQRS、数据同步 |
同步通信的取舍:
异步通信的取舍:
Hugo 的实践建议:项目中混合使用两种模式——查询走同步(REST/gRPC),命令走异步(事件/消息)。这个模式在大多数场景下都能取得良好的平衡。另外,需要特别注意消息的幂等性和编序问题。
服务网格通过 Sidecar 代理将通信治理从业务代码中剥离:
[服务A] ←→ [Sidecar Proxy] ←网络→ [Sidecar Proxy] ←→ [服务B]
能力:
在分布式环境中,一个用户请求可能穿越 5-20 个服务。传统的逐服务日志查看已不可行。
解决方案:
服务健康检查 →
├── Liveness Probe(存活检查):服务是否运行?
├── Readiness Probe(就绪检查):服务能否接受流量?
└── Startup Probe(启动检查):服务是否初始化完成?
原则:先定义服务间接口契约,再实现具体功能。
优势:
| 策略 | 方法 | 优势 | 劣势 |
|---|---|---|---|
| URI 版本化 | /v1/users |
简单直观 | URI 膨胀 |
| Header 版本化 | Accept: v2 |
同一 URI | 调试不便 |
| Schema 兼容性 | 兼容变更 = 新字段 | 无需版本 | 需要严格规范 |
Hugo 的内部约定:
- 新增可选字段 → 兼容变更(无需升级依赖方)
- 删除/重命名字段 → 不兼容变更(必须升级版本号)
- 使用 protobuf/GraphQL 的 Schema 定义自动检查兼容性
- CI 流水线中嵌入 Schema 兼容性检查门禁
Conway 定律:系统的结构最终会反映创建它的团队的结构。
实践指导:
推荐实践:
ADR 模板:
# ADR-005:使用 Saga 模式处理跨服务事务
## 日期
2025-06-15
## 状态
已接受
## 上下文
订单系统拆分后,创建订单需要依次调用库存、支付、物流服务,
需要一个分布式事务方案。
## 决策
采用协调器 Saga 模式。由订单服务作为 Saga 协调器,
通过事件驱动各参与服务的执行和补偿。
## 权衡
- 优点:事务逻辑集中,便于管理
- 缺点:协调器成为单点,需要高可用配置
- 平衡:配合事件队列 + 重试机制降低可用性风险
## 后果
所有参与服务需要提供补偿接口。
需要对协调器做幂等性设计。
Hugo 的实践:ADR 是团队架构知识的最重要的持久化载体。我要求所有架构决策必须有 ADR,且每个 ADR 必须包含明确的权衡分析(Trade-Off Analysis)。这样做最大的好处是:六个月后没有人会追问"为什么当初要这么设计"——ADR 已经记录了当时的上下文和取舍理由。
[PC Web] → [单体电商应用 + 共享数据库]
[Mobile] ↗
痛点:
[API 网关] → [用户服务] [商品服务] [订单服务] [支付服务] [物流服务]
↓ ↓ ↓ ↓ ↓
[用户DB] [商品DB] [订单DB] [支付DB] [物流DB]
关键决策:
结果:
[用户服务] → [订单服务(协调器)]
↓ (事件)
[库存服务 ← 消息队列 → 支付服务 → 物流服务]
关键决策:
结果:
本书最重要的信息:不要在软件架构中寻找"最佳实践",而要培养权衡意识。
在大多数系统中,功能需求决定"能做什么";而非功能性需求(性能、安全、可用性、可维护性)决定"能做多久"。不要在架构评审中忽略非功能性需求的权衡。
本文基于《Software Architecture: The Hard Parts》(Neal Ford, Mark Richards, Pramod Sadalage, Zhamak Dehghani)的核心内容整理撰写。这是每一位拥有五年以上经验的软件架构师的必读书籍。