返回架构笔记
Arch Day 116

Arch Day 116: 软件架构面试专题

Arch Day 116: 软件架构面试专题

2026-07-24
第四阶段 - 高阶融合
面试软件架构DDD微服务事件驱动CQRSSagaATAMADR

日期: 2026-07-24 (Day 116) 阶段: 第四阶段 - 高阶融合 标签: #面试 #软件架构 #DDD #微服务 #事件驱动 #CQRS #Saga #ATAM #ADR


概述

软件架构面试是技术面试中难度最高的环节——面试官不仅考察你对架构模式的理解,更考察你在实际场景中的权衡和决策能力。本专题整理15道软件架构高频面试题,从DDD到微服务、从CQRS到Event Sourcing、从分布式事务到架构评估,覆盖架构师核心知识域。

每道题包含30秒版本(电梯演讲)、2分钟版本(标准回答)、追问准备(深度展示)。


题目 1:DDD中限界上下文如何识别?

30秒版本

限界上下文(Bounded Context)是DDD战略设计的核心——它定义了一个模型的适用边界。识别方法有三:一是语言边界法,当同一个词在不同团队含义不同时(如"账户"在存款部门和信用卡部门含义不同),就是两个上下文;二是Event Storming法,通过领域事件流中的"枢纽事件"和"角色转换"识别边界;三是业务能力法,每个Level 1业务能力通常对应一个限界上下文。

2分钟版本

三种识别方法详解

方法1: 语言边界法 (Linguistic)
  ────────────────────
  核心原则: 当同一术语在不同团队有不同含义时,画一条线

  示例(电商):
  "商品" 在商品域 = SKU + 属性 + 描述 + 图片
  "商品" 在订单域 = SKU + 数量 + 价格
  "商品" 在库存域 = SKU + 库位 + 数量
  → 三个不同的限界上下文,各有自己的"商品"模型

方法2: Event Storming (事件风暴)
  ────────────────────
  步骤:
  1. 列出所有领域事件 (橙色便利贴)
  2. 按时间线排列
  3. 找到"枢纽事件" (多个后续流程的起点)
  4. 找到"角色/参与者变化" (不同人负责不同阶段)
  5. 在枢纽事件处画边界 → 限界上下文

  示例: "订单已支付"是枢纽事件
    → 之前: 订单上下文 (客户+购物流程)
    → 之后: 履约上下文 (仓库+物流)
    → 两个限界上下文

方法3: 业务能力映射 (Capability Mapping)
  ────────────────────
  Level 1能力 ≈ 限界上下文 (近似映射)

  客户管理能力 → 客户上下文
  订单管理能力 → 订单上下文
  库存管理能力 → 库存上下文
  支付处理能力 → 支付上下文

验证标准(识别后如何验证):

  • 团队对齐:一个上下文应该由一个团队完整负责(Conway定律)
  • 独立部署:修改一个上下文不需要修改另一个
  • 数据自治:每个上下文有自己的数据存储
  • 业务完整性:一个上下文能独立完成一个有意义的业务功能

追问准备

追问: "限界上下文和微服务是1:1关系吗?"

不一定。初期可以一个上下文包含多个微服务(如订单上下文拆成订单服务+退款服务);但反过来,一个微服务不应该跨越多个上下文。经验法则:限界上下文是服务边界的上限,可以在上下文内进一步拆分,但不要合并不同上下文。

追问: "识别错误的上下文边界怎么办?"

这很常见。信号是:两个服务之间频繁同步调用(耦合太紧,可能应该合并)、一个服务太大改动困难(可能应该拆分)。修正方法:重新做Event Storming,用实际开发中的痛点反推边界调整。DDD的边界设计是迭代的,不是一次性的。


题目 2:微服务拆分的原则?什么时候不该拆?

30秒版本

拆分原则:单一职责(一个服务做一件事)、团队对齐(一个团队拥有1-3个服务)、数据自治(不共享数据库)、独立部署(修改部署不影响其他服务)。不该拆的信号:团队少于10人(运维成本大于收益)、强一致性需求(跨服务事务太复杂)、紧密耦合的业务逻辑(拆了反而更复杂)。"先单体后拆分"是最安全的策略——先用模块化单体证明边界,再按需拆微服务。

2分钟版本

拆分原则(优先级排序)

P0: 业务域边界 (DDD限界上下文)
  └── 最重要的拆分依据,确保服务边界有业务意义

P1: 团队边界 (Conway定律)
  └── 一个团队(5-8人)负责1-3个服务
  └── 跨团队的服务一定有协调成本

P2: 变更频率 (差异化演进)
  └── 频繁变更的(如促销) vs 稳定的(如账户) 应该分开
  └── 允许不同节奏发布

P3: 扩展性需求 (资源隔离)
  └── 流量差异大的应该分开(搜索vs管理后台)
  └── 资源类型不同的分开(CPU密集型vs内存密集型)

P4: 安全/合规边界 (隔离敏感数据)
  └── PCI DSS要求支付数据隔离
  └── GDPR要求个人数据可追溯

不该拆的场景

场景原因替代方案
团队<10人运维成本>收益模块化单体
强一致性业务分布式事务太复杂单进程+DDD模块
紧密耦合逻辑拆了变成"分布式单体"先解耦再拆
高频同步调用网络延迟累积合并为一个服务
数据需要JOIN拆了要跨服务查询共享读模型(CQRS)
刚开始的项目边界不清楚先Monolith后拆

2025-2026趋势——模块化单体回潮

行业趋势:
  2015-2020: "微服务万岁" → 过度拆分
  2020-2023: "反思期" → 分布式单体之痛
  2023-2026: "模块化单体" → 理性回归

模块化单体 = 单体部署 + DDD模块化
  ├── 模块间通过接口通信(非直接依赖)
  ├── 每个模块有独立的数据Schema
  ├── 需要时可以拆成独立服务
  └── 保持了部署简单+本地事务的优势

代表项目: Shopify(从微服务回归模块化单体)

追问准备

追问: "你遇到过微服务拆分失败的案例吗?"

有。某项目将"订单+支付+库存"拆成三个独立服务,但业务要求下单时必须同步完成库存扣减和支付冻结。结果跨服务的Saga极其复杂,补偿逻辑比正常逻辑还多。最终我建议将订单和库存合并为"交易服务",支付保持独立。教训:不要为了拆而拆,业务一致性边界 > 技术架构理想。


题目 3:分布式事务方案对比(2PC/TCC/Saga)?

30秒版本

三种方案各有适用场景:2PC(两阶段提交)强一致但性能差,适合数据库层面;TCC(Try-Confirm-Cancel)性能好但侵入性强,适合资金类业务;Saga(编排/协同)最灵活但最终一致,适合长流程业务。我的选择优先级是:能用本地事务解决就不用分布式事务,必须跨服务时优先Saga(简单场景)或TCC(资金场景),2PC基本不推荐。

2分钟版本

三种方案对比

维度2PCTCCSaga
一致性强一致强一致最终一致
性能差(锁资源)好(预留不锁)最好(异步)
侵入性低(DB驱动)高(需改业务)中(补偿逻辑)
复杂度
适用场景DB层面资金/高价值长流程/一般业务
隔离性需自己处理需自己处理
回滚方式自动Cancel操作补偿事务
超时处理协调者管理自定义自定义

详细机制

2PC (Two-Phase Commit):
  Phase 1 (Prepare): 协调者问各参与者"能提交吗?"
  Phase 2 (Commit/Rollback): 全部Yes→提交,任一No→回滚

  问题: 协调者单点 / 参与者锁资源等待 / 网络分区时阻塞
  适用: 数据库XA事务 (同一DB集群内)

TCC (Try-Confirm-Cancel):
  Try: 预留资源 (冻结余额/锁定库存)
  Confirm: 确认使用 (扣款/扣库存)
  Cancel: 释放预留 (解冻/释放)

  优势: 不锁DB资源,性能好
  代价: 每个服务需要实现T/C/C三个接口
  适用: 资金账务 (银行/支付)

Saga:
  编排式: 中央编排器控制流程
    Create Order → Reserve Inventory → Charge Payment
    如果Payment失败 → Release Inventory → Cancel Order

  协同式: 事件驱动,无中央控制
    OrderCreated → InventoryReserved → PaymentCharged
    PaymentFailed → InventoryReleased → OrderCancelled

  优势: 最灵活,无资源锁定
  代价: 中间状态可见(隔离性差)
  适用: 电商下单/物流/任何长流程

我的决策树

需要跨服务数据一致性吗?
  │
  ├── No → 本地事务 + 领域事件(异步最终一致)
  │
  └── Yes → 涉及资金/高价值?
             │
             ├── Yes → TCC (严格一致性)
             │
             └── No → 流程多长?
                      │
                      ├── 短(2-3步) → 编排式Saga + Outbox
                      │
                      └── 长(4步+) → 编排式Saga + 状态机

追问准备

追问: "Saga的隔离性问题如何解决?"

Saga没有原生隔离性——中间状态对外可见(如库存已扣但订单未创建)。三种缓解方案:(1) 语义锁——在资源上标记"处理中"状态,其他操作看到这个状态就等待或跳过;(2) 业务补偿——设计业务流程时就考虑中间状态是可接受的;(3) 版本号/乐观锁——通过版本号防止并发冲突。


题目 4:Event Sourcing的优缺点?

30秒版本

Event Sourcing不存储当前状态,而是存储所有状态变更事件——通过回放事件重建状态。优点是完整审计追踪、时间旅行(查看任意时刻状态)、天然支持CQRS、事件可驱动下游系统。缺点是查询复杂(需要投影/物化视图)、Schema演进困难(旧事件格式变了怎么办)、存储量大、调试困难。适合金融交易/审计/复杂领域模型,不适合简单CRUD。

2分钟版本

核心概念

传统方式: 存储当前状态
  Account: { id: 1, balance: 800 }
  → 只知道"现在余额800",不知道怎么来的

Event Sourcing: 存储变更事件
  Event 1: AccountCreated { id: 1, balance: 0 }
  Event 2: MoneyDeposited { amount: 1000 }
  Event 3: MoneyWithdrawn { amount: 200 }
  → 回放事件: 0 + 1000 - 200 = 800
  → 知道完整历史,可以回到任意时刻

优点详解

优点说明适用场景
完整审计每次变更都有记录,不可篡改金融/合规
时间旅行可以重建任意时刻的状态调试/分析
事件驱动事件天然可以通知下游微服务集成
读写分离天然支持CQRS高读写比
调试回放可以在测试环境回放生产事件问题复现

缺点详解

缺点说明缓解方案
查询复杂不能直接查"余额>1000的账户"建投影/物化视图
Schema演进旧事件格式变了怎么办Upcaster(事件版本转换)
存储量大事件只增不删快照(Snapshot)机制
学习曲线团队不熟悉思维模式培训+渐进采纳
最终一致投影有延迟同步投影(牺牲性能)
调试困难状态是计算出来的好的工具+日志

快照优化

没有快照: 回放全部事件
  Event 1 → Event 2 → ... → Event 10000 → 当前状态
  (重建一次需要回放10000个事件,慢!)

有快照: 从最近快照开始
  Snapshot@Event 9900 → Event 9901 → ... → Event 10000
  (只需回放100个事件,快!)

快照策略:
  - 每N个事件创建一次快照(如每100个)
  - 或定时创建(如每小时)
  - 快照 + 后续事件 = 当前状态

追问准备

追问: "Event Sourcing和Event-Driven Architecture有什么区别?"

完全不同的概念。Event Sourcing是一种存储模式——用事件作为数据的一等公民。Event-Driven Architecture是一种通信模式——用事件解耦服务间通信。两者经常一起用(Event Sourcing产生的事件发布到消息总线),但可以独立使用——你可以用Event-Driven但不用Event Sourcing(服务间通过事件通信,但内部存储的是状态)。


题目 5:CQRS什么时候用?什么时候是过度设计?

30秒版本

CQRS(命令查询职责分离)在读写模型差异大时最有价值——比如写是复杂的领域操作(创建订单涉及库存/促销/支付),读是简单的列表查询(订单列表+搜索+统计)。过度设计的信号:读写模型几乎一样(简单CRUD)、团队不熟悉(学习成本高)、业务简单不需要事件驱动。我的经验是:10个服务中大约2-3个需要CQRS,不要全局使用。

2分钟版本

CQRS核心概念

传统架构:
  客户端 → Service → DB (读写同一模型)

CQRS架构:
  客户端 → Command Handler → Write Model (领域模型)
                                    │
                                 Event
                                    │
                                    ▼
  客户端 ← Query Handler ← Read Model (查询优化模型)

Write Model: 丰富的领域模型,强调业务规则和不变量
Read Model: 扁平的查询模型,为具体查询场景优化

适用场景

场景为什么适合
读写比差距大(100:1)读模型可以用缓存/ES/Redis,写模型保持一致性
读写模型差异大写是聚合根操作,读是跨聚合JOIN
多种查询视图同一数据需要不同的展示方式(列表/报表/搜索)
高并发读读模型可以多副本/缓存/CDN
事件溯源系统Event Sourcing天然需要CQRS(事件→投影→查询)

不适用(过度设计)的场景

场景为什么过度替代方案
简单CRUD读写模型一样标准MVC
团队不熟学习成本>收益先学习再引入
业务简单复杂度在别处直接Repository
强一致性读写延迟不可接受同步投影(部分CQRS)
数据量小一个DB就能搞定单库+缓存

实现复杂度梯度

Level 0: 无CQRS
  → Service → Repository → DB

Level 1: 同库分模型 (最简CQRS)
  → Command → Write Table
  → Query → Read View (DB视图)

Level 2: 异库分模型 (标准CQRS)
  → Command → Write DB (MySQL)
  → Event → Read DB (Elasticsearch)

Level 3: Event Sourcing + CQRS (完整版)
  → Command → Event Store
  → Event → Projection → Read DB

建议: 从Level 1开始,按需升级

追问准备

追问: "CQRS的读模型延迟怎么处理?"

三种方案按场景选:(1) Write-then-Read——写完后返回写模型的数据(不查读模型);(2) 版本号轮询——前端带上版本号,如果读模型版本号不够新就等待或从写模型读;(3) 同步投影——关键场景用同步方式更新读模型(牺牲一些性能保证即时一致)。绝大多数场景用户不会注意到毫秒级的延迟。


题目 6:如何评估架构方案质量(ATAM)?

30秒版本

ATAM(架构权衡分析方法)是SEI提出的系统化架构评估方法。核心流程是:收集利益相关方的质量属性需求(如性能/可用性/安全),将需求转化为具体场景,针对架构方案分析每个场景,识别风险点(可能出问题的决策)、敏感点(影响多个质量属性的决策)、权衡点(改善A但恶化B的决策)。ATAM的价值不是"评分",而是让所有人对架构风险达成共识。

2分钟版本

ATAM流程

Phase 1: 展示 (Presentation)
  1.1 架构师介绍架构方案 (45min)
  1.2 识别架构方法/模式/关键决策

Phase 2: 调查与分析 (Investigation)
  2.1 利益相关方提出质量属性场景
      示例场景:
      "双11零点,系统QPS从5万飙到50万,
       响应时间仍保持在200ms以内"

  2.2 分析场景与架构的匹配
      这个场景触发了哪些架构决策?
      这些决策的风险是什么?

Phase 3: 测试 (Testing)
  3.1 针对每个场景,评估架构的响应
  3.2 识别:
      - 风险 (Risks): 可能导致质量属性不达标的决策
      - 非风险 (Non-risks): 已经验证的安全决策
      - 敏感点 (Sensitivity): 微小变化影响大的点
      - 权衡点 (Tradeoffs): A↑则B↓的决策

Phase 4: 报告 (Reporting)
  输出风险清单 + 权衡清单 + 改进建议

简化版ATAM(适合团队内部使用)

不必搞3-4天的正式评审,可以用"轻量ATAM":

1. 架构师准备: ADR + 架构图 + 质量属性列表
2. 半天工作坊:
   - 30min: 架构方案展示
   - 60min: 场景走查(按质量属性逐个讨论)
   - 30min: 风险识别和优先级排序
3. 产出: 风险登记簿 + 行动项

频率: 每个重大架构决策做一次 (每季度1-2次)

追问准备

追问: "ATAM和Architecture Review的区别?"

ATAM是方法论——有完整的流程、角色和产出。Architecture Review是活动——形式可以多样。ATAM可以作为Architecture Review的一种方法。日常Review可以更轻量(ADR评审+场景走查),重大决策用完整ATAM。关键不是方法论本身,而是"有没有系统化地评估架构质量"。


题目 7:架构决策如何记录和治理(ADR)?

30秒版本

ADR(Architecture Decision Record)是记录架构决策的轻量文档。每个ADR包含:标题(一句话决策)、上下文(为什么需要决策)、决策(选了什么)、被否决的选项(为什么不选)、后果(正面/负面/风险)。ADR存在代码仓库中(和代码一起版本控制),编号递增,状态有Proposed/Accepted/Deprecated/Superseded。治理方面,新服务/技术选型/大变更必须有ADR,在PR中评审。

2分钟版本

ADR模板

# ADR-XXXX: [标题——一句话决策]

## 状态
Proposed | Accepted | Deprecated | Superseded by ADR-YYYY

## 上下文
为什么需要做这个决策?当前面临什么问题?

## 决策
我们决定...

## 被否决的选项
- 选项A: ... → 不选因为...
- 选项B: ... → 不选因为...

## 后果
### 正面
- ...

### 负面
- ...

### 风险
- ...

## 合规性
与架构原则 AP-XX 一致/冲突

ADR治理体系

┌─────────────────────────────────────────────┐
│              ADR治理金字塔                    │
├─────────────────────────────────────────────┤
│                                             │
│  L1: 团队级ADR (团队自主)                    │
│      范围: 服务内部技术选择                   │
│      评审: Tech Lead审批                     │
│      示例: "用Redis还是Memcached做缓存"      │
│                                             │
│  L2: 域级ADR (跨团队协调)                    │
│      范围: 跨服务的架构决策                   │
│      评审: 架构评审会议                       │
│      示例: "域间通信用gRPC还是REST"           │
│                                             │
│  L3: 组织级ADR (架构委员会)                  │
│      范围: 全局架构方向                       │
│      评审: 架构委员会                        │
│      示例: "上云策略选AWS还是多云"            │
└─────────────────────────────────────────────┘

工具链

  • 存储:Git仓库 docs/adr/ 目录(和代码一起管理)
  • 编号:自增编号 ADR-0001, ADR-0002
  • 工具adr-tools CLI / Markdown模板
  • 搜索:GitBook/Confluence汇总索引
  • CI检查:PR包含架构变更时自动提醒"需要ADR吗?"

追问准备

追问: "团队不愿意写ADR怎么办?"

三步走:(1) 降低门槛——ADR不是论文,5分钟能写完的模板最好;(2) 展示价值——找一个"之前没有ADR导致重复讨论同一问题"的案例,让大家感受到痛点;(3) 融入流程——在PR模板中加"是否需要ADR?"的checklist,让写ADR成为自然习惯。


题目 8:如何设计高可用架构?

30秒版本

高可用的核心是"消除单点故障+快速恢复"。四层策略:应用层(无状态+多副本+负载均衡)、数据层(主从复制+读写分离+自动故障切换)、基础设施层(多AZ部署+异地灾备)、流程层(超时+熔断+降级+限流)。可用性目标决定架构复杂度——99.9%(年宕机8.7小时)用主从足够,99.99%(53分钟)需要多AZ,99.999%(5分钟)需要异地多活。

2分钟版本

可用性等级与架构

等级年宕机架构要求成本
99%87.6小时基本冗余$
99.9%8.7小时主从+自动切换$$
99.99%53分钟多AZ+自动failover$$$
99.999%5分钟异地多活$$$$

四层高可用策略

Layer 1: 应用层高可用
  ├── 无状态设计 (Session外部化到Redis)
  ├── 多实例部署 (至少3个实例)
  ├── 健康检查 (Liveness + Readiness)
  ├── 优雅停机 (Pre-stop hook,处理完当前请求)
  └── 版本控制 (蓝绿/金丝雀发布)

Layer 2: 数据层高可用
  ├── MySQL: 主从复制 + MHA/Orchestrator自动切换
  ├── Redis: Sentinel(中小) / Cluster(大规模)
  ├── ES: 多副本 + 跨AZ分布
  ├── Kafka: 3副本 + ISR机制
  └── 数据备份: 增量+全量,跨AZ存储

Layer 3: 基础设施层高可用
  ├── 多AZ部署 (同一Region的2-3个AZ)
  ├── 负载均衡 (L4/L7 LB,自动摘除不健康节点)
  ├── DNS容灾 (多Region DNS记录)
  └── CDN (静态资源全球分发)

Layer 4: 流程层高可用
  ├── 超时控制 (合理的超时设置,避免级联等待)
  ├── 熔断器 (Circuit Breaker,防止雪崩)
  ├── 降级策略 (核心功能保障,非核心可降级)
  ├── 限流 (令牌桶/滑动窗口,保护后端)
  └── 预案 (故障预案+定期演练)

故障处理原则

  1. 快速发现:监控+告警,秒级发现异常
  2. 自动恢复:自动切换/自动扩容/自动重启
  3. 人工兜底:自动恢复失败时的应急预案
  4. 事后改进:Postmortem + 改进项跟踪

追问准备

追问: "异地多活怎么做?最大的挑战是什么?"

异地多活最大的挑战是数据一致性。两种模式:(1) 同城双活——两个AZ同步写入(强一致),延迟低(<2ms);(2) 异地多活——多个Region各自写入,异步同步(最终一致),延迟高(>50ms)。异地多活需要解决:数据路由(用户数据归属哪个Region)、冲突解决(同一数据被两个Region修改)、故障切换(一个Region挂了用户自动切到另一个)。核心思路是"单元化"——按用户维度将数据和请求路由到固定Region,避免跨Region写入冲突。


题目 9:可观测性三支柱如何落地?

30秒版本

可观测性三支柱:Metrics(度量——"系统怎么样")、Logs(日志——"发生了什么")、Traces(追踪——"为什么慢")。落地关键是统一采集(OpenTelemetry SDK)、统一存储(Prometheus+Loki+Tempo 或 Elastic+Jaeger)、统一展示(Grafana)。比工具更重要的是规范——统一的日志格式、统一的Metric命名、统一的Trace上下文传播。

2分钟版本

三支柱关联

问题: "用户反馈下单慢"

1. Metrics → 发现: 订单服务P99延迟从100ms升到5s
   (Grafana仪表盘, 发现问题)

2. Traces → 定位: 订单服务→库存服务的调用耗时4.8s
   (Jaeger追踪, 缩小范围)

3. Logs → 根因: 库存服务日志显示"DB连接池耗尽"
   (Loki日志, 找到根因)

三者串联:
  Metric告警 → 关联TraceID → 查到具体慢调用 →
  从TraceID查到日志 → 找到根因

统一技术栈(2025-2026推荐)

采集层: OpenTelemetry (统一SDK)
  ├── Auto-instrumentation (自动埋点)
  ├── Manual instrumentation (自定义埋点)
  └── Context propagation (W3C Trace Context)

存储层:
  ├── Metrics: Prometheus / VictoriaMetrics
  ├── Logs: Loki / Elasticsearch
  └── Traces: Tempo / Jaeger

展示层: Grafana (统一Dashboard)
  ├── Metric → Log 联动 (通过时间+标签)
  ├── Trace → Log 联动 (通过TraceID)
  └── 统一告警 (Grafana Alerting)

规范比工具更重要

规范说明示例
日志格式JSON结构化{"traceId":"xxx","level":"ERROR","msg":"..."}
Metric命名业务域+类型+单位order_create_duration_seconds
Trace标签统一标签键service.name, http.method, db.system
告警分级P0-P3P0: 影响用户, P1: 影响性能, P2: 需关注

追问准备

追问: "微服务太多,链路追踪数据量爆炸怎么办?"

采样策略:(1) 头部采样——按概率采样(如1%),简单但可能漏掉异常请求;(2) 尾部采样——所有请求都记录,但只保存异常/慢的链路(推荐);(3) 动态采样——正常时低采样率,发现异常时自动提高采样率。2025-2026趋势是用Grafana Tempo + 尾部采样,只保存"有价值"的链路,存储成本降低90%。


题目 10:API设计最佳实践?

30秒版本

API设计的核心原则:RESTful语义(名词资源+HTTP动词)、版本化(URL/Header)、幂等性(重复调用结果一致)、分页(大列表必须分页)、错误码标准化(业务码+HTTP码)、安全(OAuth2+速率限制)。对内服务间通信优先gRPC(性能好+强类型),对外API用REST(通用+易理解)。API Gateway做统一鉴权/限流/日志/版本路由。

2分钟版本

API设计规范

1. 资源命名 (RESTful)
   ✓ GET /orders         (查询订单列表)
   ✓ GET /orders/{id}    (查询单个订单)
   ✓ POST /orders        (创建订单)
   ✓ PUT /orders/{id}    (更新订单)
   ✓ DELETE /orders/{id}  (删除订单)

   ✗ POST /createOrder   (动词命名)
   ✗ GET /getOrderById   (冗余)

2. 版本策略
   方式A: URL路径 /v1/orders (简单直观,推荐)
   方式B: Header Accept: application/vnd.api+v1 (优雅但复杂)
   方式C: Query参数 /orders?version=1 (不推荐)

3. 幂等设计
   GET/DELETE: 天然幂等
   POST: 用幂等键(Idempotency-Key Header)
   PUT: 用版本号(If-Match/ETag)

4. 错误响应标准化
   {
     "code": "ORDER_NOT_FOUND",
     "message": "订单不存在",
     "details": { "orderId": "12345" },
     "traceId": "abc-123-def"
   }

5. 分页
   GET /orders?page=1&size=20&sort=createdAt:desc
   响应: { data: [...], pagination: { total, page, size, pages }}

6. 安全
   ├── 认证: OAuth2 / JWT
   ├── 限流: Rate Limiting (Token Bucket)
   ├── 加密: TLS 1.3
   └── 输入验证: 防注入/XSS

gRPC vs REST 选择

维度RESTgRPC
协议HTTP/1.1 或 2HTTP/2
序列化JSONProtobuf
性能一般高(5-10倍)
强类型弱(需要文档)强(proto文件)
浏览器原生支持需要gRPC-Web
流式SSE/WebSocket原生双向流
适用对外API内部服务间

追问准备

追问: "API向后兼容怎么保证?"

三条规则:(1) 只加字段不删字段(新增的字段设默认值);(2) 不改字段类型(string不能变int);(3) 不改语义(同一个字段不能改含义)。如果必须做破坏性变更——发布新版本API(v2),老版本继续维护一段时间(至少6个月),通知客户端迁移,最终下线老版本。用API Gateway做版本路由最优雅。


题目 11:如何做性能优化?

30秒版本

性能优化的方法论:先度量后优化(不要猜)、先定位瓶颈后解决(80/20法则——20%的问题导致80%的性能损耗)。常见优化路径:数据库(慢查询优化/索引/分库分表)、缓存(多级缓存/热点预加载)、应用层(异步化/池化/批量化)、网络(减少调用次数/压缩/CDN)。目标是在成本可接受的范围内达到业务要求的性能指标,不是无限追求极致性能。

2分钟版本

性能优化方法论

Step 1: 定义目标
  "首页加载时间从3s降到1s以内"
  "下单接口P99从500ms降到200ms"

Step 2: 度量现状
  ├── APM工具: 链路追踪看各环节耗时
  ├── DB慢查询: 找到Top 10慢SQL
  ├── 压测: 确定当前极限QPS
  └── 火焰图: CPU/内存热点分析

Step 3: 定位瓶颈 (通常80%的问题在20%的代码)
  常见瓶颈排序:
  1. DB查询 (50%的性能问题)
  2. 网络调用 (20%)
  3. 缓存未命中 (15%)
  4. 应用层计算 (10%)
  5. 序列化/GC (5%)

Step 4: 针对性优化
  DB优化: 索引/SQL重写/分库分表/读写分离
  缓存优化: L1本地缓存+L2分布式缓存+预热
  应用优化: 异步化/批量化/池化/算法优化
  网络优化: 减少调用/合并请求/压缩/CDN

Step 5: 验证和监控
  压测验证 → 灰度发布 → 生产监控

多级缓存方案

请求 → L1本地缓存(Caffeine) → 命中? → 返回
         │ 未命中
         ▼
       L2分布式缓存(Redis) → 命中? → 写入L1 → 返回
         │ 未命中
         ▼
       DB查询 → 写入L2 → 写入L1 → 返回

TTL策略:
  L1: 短TTL(30s~5min),容忍少量不一致
  L2: 中TTL(5min~1h),主动失效+TTL双保险

追问准备

追问: "缓存穿透/击穿/雪崩怎么处理?"

穿透(查不存在的数据):布隆过滤器+缓存空值。击穿(热Key过期瞬间大量请求打到DB):永不过期+后台刷新 或 互斥锁(只放一个请求穿透到DB)。雪崩(大量Key同时过期):随机化TTL(基础TTL+随机偏移)+预热+限流。


题目 12:安全架构的核心原则?

30秒版本

安全架构的核心原则:零信任(不信任任何网络位置,每次访问都验证)、最小权限(只给必需的权限)、纵深防御(多层安全措施,一层被突破还有下一层)、安全左移(在设计和编码阶段就考虑安全,而非上线后补)。落地措施包括:认证(OAuth2+MFA)、授权(RBAC/ABAC)、加密(传输TLS+存储AES)、审计(所有敏感操作留痕)、漏洞管理(SAST/DAST/SCA自动扫描)。

2分钟版本

零信任架构(2025-2026主流)

传统安全模型 (城堡模式):
  外部 → 防火墙 → 内部(信任区)
  问题: 一旦突破防火墙,内部横向移动无阻

零信任模型:
  每个请求都需要:
  1. 身份验证 (Who are you?)
  2. 设备验证 (What device?)
  3. 上下文评估 (Where/When/How?)
  4. 最小权限 (What can you access?)
  5. 持续验证 (Still trusted?)

落地要素:
  ├── Service Mesh (mTLS,服务间加密)
  ├── IAM (统一身份管理)
  ├── API Gateway (统一鉴权+限流)
  ├── Secret Management (Vault管理密钥)
  └── Network Policy (K8s网络策略,微隔离)

安全左移

阶段安全活动工具
设计威胁建模(STRIDE)Microsoft Threat Modeling
编码安全编码规范IDE插件(SonarLint)
构建SAST(静态扫描)SonarQube/Semgrep
测试DAST(动态扫描)OWASP ZAP
部署镜像扫描+合规检查Trivy/Snyk
运行WAF+IDS+审计CloudFlare/Falco

追问准备

追问: "如何应对供应链攻击(如Log4j这类)?"

三层防御:(1) 预防——用SCA工具(如Snyk/Dependabot)扫描所有依赖,自动告警已知漏洞;(2) 检测——SBOM(软件物料清单)记录所有组件版本,一旦新漏洞披露立即知道是否受影响;(3) 响应——预案和流程,收到CVE后X小时内评估,Y小时内修复。Log4j事件的教训是:你必须知道自己用了什么(SBOM),否则无法快速响应。


题目 13:如何管理技术债?

30秒版本

技术债管理的关键是"看见它"——看不见的债务才是最危险的。四步走:识别(代码扫描+团队反馈+架构评审)、量化(用修复成本/不修复的每年维护成本来量化)、优先级(关键路径上的债>非关键路径的)、还债(每个Sprint分配20%容量处理技术债)。不要追求零债务——适度的技术债是合理的商业决策,关键是有意识地管理而非无意识地积累。

2分钟版本

技术债四象限

               故意的                  无意的
           ┌──────────────┬──────────────┐
  审慎的   │ "我们知道快速   │ "我们现在知道   │
           │  发布会欠债,   │  当初应该怎么   │
           │  以后再优化"    │  设计了"         │
           │               │               │
           │ → 记录+计划还债│ → 重构计划     │
           ├──────────────┼──────────────┤
  鲁莽的   │ "赶紧上线,    │ "什么是分层    │
           │  管它的"       │  架构?"       │
           │               │               │
           │ → 紧急修复    │ → 培训+重写    │
           └──────────────┴──────────────┘

量化方法

技术债利息 = 因为债务导致的额外成本/年
  ├── 开发减速: "本来1天能做的功能,现在要3天" → ΔCost
  ├── 故障成本: "每月因为这个坑导致1次事故" → ΔIncident
  ├── 招聘影响: "技术栈太老,招不到人" → ΔHiring
  └── 安全风险: "老版本框架有已知漏洞" → ΔRisk

技术债本金 = 彻底修复所需的投入
  ├── 重构成本: 多少人天?
  ├── 测试成本: 回归测试范围?
  └── 风险成本: 重构过程可能引入的问题

还债优先级 = 利息 / 本金 (比值越高越优先还)
  "维护成本高+修复成本低" → 优先还
  "维护成本低+修复成本高" → 可以忍一忍

追问准备

追问: "领导说'先做需求再说',怎么争取技术债时间?"

两个策略:(1) 用数据说话——"因为技术债,过去3个月平均每个需求多花2天,相当于浪费了6个人天/月";(2) 捆绑策略——将技术债修复嵌入到需求开发中(做新需求时顺便重构相关模块),而非单独申请"重构时间"。最有效的是每个Sprint分配20%容量给技术债——这不需要领导特别批准,是团队对工程质量的基本投资。


题目 14:如何选择数据库?

30秒版本

数据库选型取决于数据模型和访问模式:关系型数据(MySQL/PostgreSQL)——ACID事务+复杂查询;文档型(MongoDB)——Schema灵活+快速迭代;键值型(Redis)——高性能缓存+计数器;列族型(HBase/Cassandra)——海量写入+时序数据;搜索型(Elasticsearch)——全文搜索+日志分析;图数据库(Neo4j)——关系图谱+推荐。选型原则:一个系统可以用多种数据库(Polyglot Persistence),但要控制种类数量(不超过3-4种)。

2分钟版本

选型决策树

你的数据模型是什么?
  │
  ├── 结构化+关系 → 需要事务?
  │                  ├── Yes → MySQL/PostgreSQL (OLTP)
  │                  └── No  → 数据量?
  │                           ├── <100GB → PostgreSQL
  │                           └── >100GB → TiDB/CockroachDB
  │
  ├── 半结构化/文档 → MongoDB / DynamoDB
  │
  ├── 键值/缓存 → Redis / Memcached
  │
  ├── 时序数据 → InfluxDB / TimescaleDB
  │
  ├── 搜索/日志 → Elasticsearch / OpenSearch
  │
  ├── 图关系 → Neo4j / Neptune
  │
  └── 海量写入/宽表 → HBase / Cassandra / ScyllaDB

Polyglot Persistence示例

电商系统:
  ├── 订单/支付: MySQL (ACID事务)
  ├── 商品搜索: Elasticsearch (全文搜索)
  ├── 用户Session: Redis (高性能)
  ├── 用户画像: MongoDB (灵活Schema)
  ├── 日志/监控: Elasticsearch (日志分析)
  └── 推荐关系: Neo4j (图查询)

原则: 为每种数据选择最合适的存储,但控制总数(≤4种)

追问准备

追问: "MySQL还是PostgreSQL?怎么选?"

技术上PostgreSQL更强(支持JSON/数组/GIS/全文搜索/物化视图),MySQL生态更成熟(工具多/人才多/云服务完善)。我的选择:新项目首选PostgreSQL(功能更全面),已有MySQL生态的团队继续用MySQL(迁移成本不值得)。2025-2026趋势是PostgreSQL市场份额持续增长,是最"全能"的关系型数据库。


题目 15:架构师如何平衡"理想架构"和"交付压力"?

30秒版本

核心原则是"做正确的权衡,而非追求完美"。三个实用策略:第一,区分"不可逆决策"和"可逆决策"——不可逆决策(如数据库选型/服务边界)必须慎重,可逆决策(如具体实现方式)可以快速做;第二,"最小可行架构"——第一版只实现核心质量属性需求,不过度设计;第三,"技术债预算"——有意识地欠债并记录还债计划。架构师的价值不是设计完美架构,而是在约束条件下做出最优决策。

2分钟版本

Jeff Bezos的决策框架

Type 1 决策 (不可逆,单向门):
  ├── 数据库选型 → 慎重,充分讨论,写ADR
  ├── 核心服务边界 → 慎重,Event Storming+评审
  ├── 云平台选择 → 慎重,PoC验证
  └── 对外API契约 → 慎重,向后兼容

Type 2 决策 (可逆,双向门):
  ├── 具体框架版本 → 快速决定,不行再换
  ├── 缓存策略 → 快速实验,数据说话
  ├── 内部API设计 → 快速迭代,逐步优化
  └── 日志格式 → 先定一个,后续统一

最小可行架构(MVA)

阶段1: MVA (最小可行架构)
  ├── 只解决当前确定的质量属性需求
  ├── 用最简单的方案实现
  ├── 记录"知道但暂不处理"的决策(ADR)
  └── 示例: 先用单体+模块化,不上微服务

阶段2: 按需演进
  ├── 业务增长触发架构演进(如QPS从1K到10K)
  ├── 每次演进只解决当前瓶颈
  ├── 更新ADR,记录演进理由
  └── 示例: 某模块成为瓶颈 → 拆为独立服务

阶段3: 持续优化
  ├── 定期架构评审(ATAM轻量版)
  ├── 技术债可视化和管理
  ├── 架构适应度函数自动监控
  └── 示例: 自动检测服务间耦合度

实用技巧

策略说明
YAGNI你不需要它(不为假想需求设计)
最后责任时刻推迟决策到不得不做的时刻(信息最充分)
可逆性优先选择容易修改的方案
增量架构小步演进而非大步重构
技术债预算每Sprint 20%容量用于偿还技术债

追问准备

追问: "如果领导要求'三个月做出来'但你觉得架构需要六个月,怎么办?"

三步沟通:(1) 分析三个月能交付什么——"三个月可以上线MVP,核心功能完整但缺少高可用/监控";(2) 明确风险——"三个月版本大促可能扛不住,需要额外1个月做性能优化";(3) 提供分阶段方案——"3个月上线MVP→第4个月性能优化→第5-6个月完善监控和安全"。不要说"做不到",而是说"可以分阶段做到"。


面试技巧总结

软件架构面试评分维度

维度权重优秀表现
权衡能力30%能分析多个方案的优劣并做出合理选择
深度理解25%不只知道"是什么",更知道"为什么"和"什么时候不该用"
实战经验20%有真实项目案例,能说出踩过的坑
全局视野15%不局限于技术,能关联业务/团队/成本
表达清晰10%结构化回答,能画图说明

15题速查表

#题目核心关键词
1限界上下文识别语言边界 / Event Storming / 能力映射
2微服务拆分何时拆何时不拆 / 模块化单体
3分布式事务2PC/TCC/Saga / 本地事务优先
4Event Sourcing优缺点 / 快照 / vs Event-Driven
5CQRS何时用 / 过度设计 / 复杂度梯度
6ATAM场景驱动 / 风险-敏感-权衡
7ADR模板 / 治理金字塔 / 存代码仓库
8高可用四层策略 / 可用性等级 / 异地多活
9可观测性三支柱 / OpenTelemetry / 采样
10API设计REST/gRPC / 幂等 / 版本化
11性能优化先度量 / 定位瓶颈 / 多级缓存
12安全架构零信任 / 安全左移 / 供应链安全
13技术债四象限 / 量化 / 20%容量
14数据库选型决策树 / Polyglot / MySQL vs PG
15理想vs现实Type1/Type2决策 / MVA / YAGNI

今日总结

关键收获

  1. 权衡是架构师的核心能力——没有"最佳架构",只有"最适合当前约束的架构"
  2. "什么时候不该用"比"什么时候用"更重要——过度设计和设计不足一样有害
  3. DDD不是微服务的同义词——限界上下文可以在模块化单体内实现
  4. Event Sourcing和CQRS是工具,不是目标——10个服务中2-3个需要就够了
  5. 架构决策要记录(ADR)——6个月后你自己都不记得当初为什么这么决定

明日预告: Day 117 - 金融领域面试专题,15道精选金融架构面试题,涵盖核心银行、支付、风控、合规、交易等核心主题。