# 04 · 十大核心架构模式
> 模式不是用来炫技的咒语,而是前人替你踩过坑后留下的「成熟解法」。学模式,是为了**手里有牌**——遇到问题时,脑子里能立刻浮现几种打法,而不是从零硬想。
---
## 先说清楚:什么是「架构模式」,以及为什么要学它
你写代码会遇到「这段逻辑似曾相识」的感觉;架构也一样。**同一类问题,在无数系统里反复出现**:怎么把复杂度切开?怎么让系统在流量洪峰下不被冲垮?怎么让读和写互不拖累?
每当一个问题被足够多的人、用足够多的方式解决过,其中那些**反复被证明有效**的解法,就沉淀成了「模式」。模式是被验证过的套路,是社区的共识词汇。
学模式有两层价值:
1. **手里有牌**:遇到问题不至于裸奔。你知道「哦,这是个典型的读写分离场景」,而不是对着白板发呆。
2. **沟通带宽**:你对同事说「这里走事件驱动」,他立刻懂你指的是哪一套结构、有哪些含义。模式是架构师之间的「行话」,一个词省下半小时解释。
但请把下面这句话刻在脑子里,它是本章的灵魂:
> **模式是工具,不是目标。** 没有「高级模式」和「低级模式」之分,只有「合不合适」之分。
> 一个用对了的单体,远比一个用错了的微服务高级。
下面逐个过这十张「牌」。每张牌我都会讲三件事:**①它解决什么问题 ②它长什么样 ③它的代价 / 什么时候千万别用**。第三点最重要——**看不到代价,就等于没学会这个模式**。
---
## 1. 分层架构(Layered)
**① 解决什么问题**
最朴素、最普遍的复杂度切分方式:把系统按「关注点」横向切成几层,每层只跟相邻层打交道。典型是三层——表现层(管界面/接口)、业务层(管规则)、数据层(管存取)。
它解决的是「**别把所有东西搅成一锅粥**」。界面逻辑、业务规则、数据库操作如果全揉在一起,改一处崩一片。分层给了你「**关注点分离**」:改界面不用碰数据库,换数据库不用动业务规则。
**② 长什么样**
```
┌─────────────────────────────┐
│ 表现层 (Presentation) │ 界面 / API,只管「怎么展示和接收」
├─────────────────────────────┤
│ 业务层 (Business Logic) │ 核心规则,「这件事该怎么算」
├─────────────────────────────┤
│ 数据访问层 (Data Access) │ 只管「怎么存、怎么取」
├─────────────────────────────┤
│ 数据库 / 外部存储 │
└─────────────────────────────┘
每层只调用「下一层」,不越级、不反向
```
**③ 代价 / 什么时候别用**
- **代价**:严格分层会带来「**穿透成本**」——一个简单查询可能要老老实实穿过每一层,写一堆「只是把数据往下传」的样板代码。层与层之间也容易出现「贫血」的传递对象。
- **什么时候别用**:它几乎永远适用,所以问题不在「用不用」,而在「**别教条**」。不要为了「纯粹」而禁止一切合理的跨层优化;也别把「分层」误当成「分布式」——分层是**代码组织**方式,不代表每层都要是独立部署的服务。
> 分层是底座,不是全部。后面几个模式,很多是「在分层的基础上,再针对某个质量问题做的专门处理」。
---
## 2. 单体(Monolith)—— 被严重低估的「正确起点」
**① 解决什么问题**
把整个应用作为**一个可部署单元**交付:所有模块在同一个进程里,一次构建、一次部署、一起上线。它解决的是「**怎么用最低的协作成本和运维成本,把东西先做出来、跑起来**」。
这里我要旗帜鲜明地表个态:**单体被严重低估了。** 过去十年「微服务」被炒得太热,以至于很多人一开口就嫌单体「土」「不先进」。这是巨大的误解。
**对绝大多数项目,单体是正确的起点,而且能撑得比你想象的久得多。** 你熟悉的很多大产品,在用户过千万时跑的依然是「一个组织良好的单体」。
**② 长什么样**
```
┌───────────────────────────────────────────┐
│ 单 体 应 用 (一个进程) │
│ │
│ ┌────────┐ ┌────────┐ ┌────────┐ │
│ │ 用户模块 │ │ 订单模块 │ │ 支付模块 │ … │ 模块之间是「函数调用」
│ └────────┘ └────────┘ └────────┘ │ 不是「网络调用」
│ │
└──────────────────────┬──────────────────────┘
│
┌────▼────┐
│ 数据库 │
└─────────┘
```
注意:单体内部**完全可以、也应该分模块**(这叫「模块化单体」)。单体 ≠ 一团乱麻,它只是说「这些模块**部署在一起**」。
**③ 代价 / 什么时候别用**
- **代价**:① 整个应用一起部署,改一行也要重新发布全部;② 无法对单个模块单独扩容(订单很忙、用户模块很闲,也得整体一起加机器);③ 一个模块的内存泄漏/崩溃可能拖垮整个进程;④ 代码库变大后,如果模块边界不清,会退化成「大泥球」。
- **什么时候别用**:当**组织和规模真的到了**单体扛不住的程度时(见下一节)。但请记住:**这些代价大多是「成长的烦恼」,是产品成功之后才需要面对的甜蜜负担。** 在你还没验证产品有没有人用之前,就为这些问题买单,是典型的过度设计。
> 给新人的硬建议:**默认从模块化单体开始。** 等你真的痛了、痛在明确的某处了,再针对那一处拆。「先拆了再说」几乎总是错的。
---
## 3. 微服务(Microservices)—— 被严重滥用的「成熟期解药」
**① 解决什么问题**
把一个大应用拆成**多个可独立开发、独立部署、独立扩容**的小服务,每个服务围绕一块业务能力,各自拥有自己的数据。它解决的核心问题其实**不是技术问题,而是组织问题**:
> **当一个团队大到几十上百号人,挤在一个单体里互相踩脚、谁也不敢动谁的代码、每次上线要全公司排队时**,微服务让每个小团队拥有自己的服务、自己的发布节奏、自己的技术选择。
请记牢这句话:**微服务首先是为了解决「人」的扩展性,其次才是「机器」的扩展性。**
**② 长什么样**
```
┌──────────────┐
│ API 网关 │ 统一入口:路由、鉴权、限流
└──┬───┬───┬───┘
│ │ │ 每个服务 = 独立部署 + 独立数据库
┌─────▼┐ ┌▼────┐ ┌▼─────┐
│用户 │ │订单 │ │支付 │ 服务之间靠「网络调用」通信
│服务 │ │服务 │ │服务 │ (而不是函数调用)
└──┬───┘ └─┬───┘ └──┬───┘
│ │ │
┌──▼─┐ ┌──▼─┐ ┌──▼─┐
│ DB │ │ DB │ │ DB │ 数据各自独立,不共享一个库
└────┘ └────┘ └────┘
```
**③ 代价 / 什么时候别用 ——(本章最重要的警告)**
微服务把单体里**简单的函数调用,变成了复杂的网络调用**。这一步跨越,引入了一整座「分布式系统」的复杂度大山:
- **网络会失败、会延迟、会乱序**。原来一个 `if` 就能解决的事,现在要处理超时、重试、幂等。
- **数据散落各处,跨服务的事务几乎不可能**(还记得吗,跨库强一致是分布式系统最难的事——详见 [05 · 数据与状态](05-数据与状态.md))。你被迫接受最终一致,引入消息、补偿、Saga 等一堆机制。
- **运维复杂度爆炸**:服务发现、链路追踪、统一日志、配置中心、容器编排……一个三人小团队,光是把这套基础设施搭起来就够呛,还没开始写业务。
- **本地调试变难**:想跑通一个流程,可能要同时起七八个服务。
**什么时候真的该用微服务?三个前提,最好同时满足:**
```
┌───────────────────────────────────────────────────┐
│ 前提一:组织规模够大 │
│ 多个团队互相阻塞、共享代码库已成为协作瓶颈 │
│ │
│ 前提二:有明确的「独立部署」需求 │
│ 某些模块需要独立的发布节奏 / 独立的扩容 / 独立的可用性 │
│ │
│ 前提三:你已经有了承接复杂度的「平台能力」 │
│ 监控、追踪、CI/CD、编排都已就位,养得起这套基础设施 │
└───────────────────────────────────────────────────┘
三者都满足 → 考虑微服务
只是「听说它先进」→ 千万别
```
> **滥用微服务的典型画面**:一个五人团队,做一个月活几千的产品,却拆出了十几个微服务,每天大半精力耗在「服务之间为什么调不通」上,业务功能寸步难行。这不是先进,这是**给自己上刑**。
>
> 行业里有句话很扎心:**「微服务是用来解决你还没遇到的问题的,代价是制造一堆你本来不会有的问题。」**
---
## 4. 事件驱动(Event-Driven)
**① 解决什么问题**
让组件之间通过「**发生了什么事**」来协作,而不是「**你去帮我做什么**」来命令。一个组件做完一件事,就广播一个「事件」(如「订单已支付」),其他关心这件事的组件各自响应,而**发出事件的人完全不知道、也不关心谁在听**。
它解决的是「**解耦**」和「**可扩展业务流程**」:下单成功后要发短信、加积分、通知物流、更新报表……如果让「下单服务」挨个去调它们,它就和所有下游死死绑在一起,每加一个动作都要改下单代码。事件驱动让下单只管喊一嗓子「订单支付了!」,后续谁想加动作,自己来订阅就行。
**② 长什么样**
```
「订单已支付」事件
┌────────┐ 发布 ┌─────────────┐ 分发
│ 订单服务 │ ──────▶ │ 事件总线/ │ ──────┬────────┬────────┐
└────────┘ │ 消息中间件 │ │ │ │
(发布者不关心 └─────────────┘ ▼ ▼ ▼
谁在消费) ┌──────┐ ┌──────┐ ┌──────┐
│发短信 │ │加积分 │ │通知物流│
└──────┘ └──────┘ └──────┘
(订阅者各自响应,互不知情)
```
**③ 代价 / 什么时候别用**
- **代价**:**整体流程变得「看不见」**。在命令式代码里,你顺着函数调用就能读懂「下单后发生了什么」;在事件驱动里,逻辑被打散到一堆订阅者中,**没有一处能看到全貌**,排查问题像破案。还要面对事件可能重复、乱序、丢失,以及「最终一致」带来的短暂不一致。
- **什么时候别用**:① 流程简单、调用关系清晰时——硬上事件驱动只会把直路绕成迷宫;② 需要「立刻拿到结果」的同步场景(用户点了按钮要马上知道成败),事件驱动天然是异步的,不适合。
> 电商是事件驱动的经典舞台:「订单支付」一个事件,扇出库存、积分、物流、风控、报表一大片。见 [电商平台模板](../templates/ecommerce-platform/README.md)。
---
## 5. 消息队列 / 异步处理
**① 解决什么问题**
在生产者和消费者之间放一个「**缓冲水池**」(队列):生产者把任务扔进去就走,消费者按自己的节奏慢慢捞出来处理。它解决三个经典问题:
- **削峰填谷**:洪峰来了,先把请求堆进队列,后端按自己能承受的速度消化,不会被瞬间冲垮。
- **异步解耦**:耗时的活儿(发邮件、转码、生成报表)不必让用户在那儿干等,扔进队列,先告诉用户「在处理了」。
- **可靠投递**:消费者挂了,任务还在队列里,重启后接着干,不丢。
**② 长什么样**
```
洪峰流量 后端按自己的节奏消费
▼ ▼ ▼ ▼ ▼ ┌────────────────────┐
┌─────────┐ │ ████████████░░░░░░ │ ┌──────────┐
│ 生产者 │───▶│ 消 息 队 列 │───▶│ 消费者(可 │
│(快速堆积)│ │ (缓冲水池,先进先出) │ │ 多个并行) │
└─────────┘ └────────────────────┘ └──────────┘
水池吸收波动,把「尖刺」抹平成「平缓水流」
```
> 同步 vs 异步的直觉:**同步是「打电话」**——你得等对方接、等对方说完;**异步是「发短信」**——发完就干别的,对方有空再回。
**③ 代价 / 什么时候别用**
- **代价**:① 引入了一个必须维护、监控、且自身不能挂的关键基础设施;② 处理变成异步,用户拿不到即时结果,产品要设计「处理中」状态;③ 必须处理「**消息可能被消费两次**」——所以消费逻辑要做成**幂等**(同一条消息处理一次和处理十次结果一样);④ 队列堆积本身会成为新的监控对象(积压了说明消费跟不上了)。
- **什么时候别用**:需要同步、即时返回结果的链路别硬塞队列;任务量很小、根本没有峰值压力时,加一个队列只是徒增运维负担。
> 视频转码是异步处理的典范:用户上传后,「转码」这种又慢又重的活必须丢进队列,后台慢慢转,不能让用户对着进度条干等。见 [视频流媒体模板](../templates/video-streaming/README.md)。
---
## 6. CQRS(读写模型分离)
**① 解决什么问题**
CQRS = Command Query Responsibility Segregation,「**命令与查询职责分离**」。一句话:**把「写」和「读」拆成两套独立的模型/路径**,各自优化。
它解决的是「**读和写的诉求根本不一样,硬用同一个模型会两头不讨好**」:写要的是规则严谨、强一致、防止脏数据;读要的是快、灵活、能按各种维度聚合。很多系统**读远多于写**(比如商品详情页,一次修改、千万次浏览),用同一套模型扛,读会被写的约束拖累,写会被读的各种索引拖累。
**② 长什么样**
```
写请求 (Command) 读请求 (Query)
│ │
▼ ▼
┌─────────┐ 数据同步/事件 ┌────────────────┐
│ 写模型 │ ─────────────────▶ │ 读模型(可多个) │
│ 规则严谨 │ (常常是异步, │ 为查询预先优化好 │
│ 强一致 │ 最终一致) │ 反范式、可缓存 │
└────┬────┘ └───────┬────────┘
▼ ▼
写库(为正确性优化) 读库/视图(为读取速度优化)
```
**③ 代价 / 什么时候别用**
- **代价**:**复杂度直接翻倍**。两套模型要维护,中间的同步通道要可靠;而且读写之间通常是**异步同步**的——意味着你写完之后,读到的可能还是旧数据(最终一致),产品和用户都要能接受这一点。
- **什么时候别用**:**绝大多数系统不需要 CQRS。** 读写压力差不多、或者数据量根本没大到需要分开优化时,上 CQRS 是自找麻烦。它是「读写严重不对称、且常规手段已经压榨干净」之后的重武器,不是默认选项。
> CQRS 和事件驱动是天生一对:写模型产生事件,读模型订阅事件来更新自己。社交信息流就常用这种思路——写(发帖)和读(刷 Feed)是完全不同的两套优化路径,见 [社交信息流模板](../templates/social-feed/README.md)。
---
## 7. 发布-订阅(Pub/Sub)
**① 解决什么问题**
Pub/Sub 是一种**通信模式**:发布者把消息发到一个「主题(topic)」,所有订阅了该主题的订阅者都会**各自收到一份**。它和「消息队列」很像但有关键区别——
> - **队列**:一条消息**只被一个**消费者处理(分活儿,你做了我就不做)。
> - **Pub/Sub**:一条消息被**所有**订阅者各收一份(广播,人人有份)。
它解决的是「**一件事要通知很多方,且通知方不想知道有哪些接收方**」的彻底解耦。它是「事件驱动」在通信层面的常见实现机制。
**② 长什么样**
```
┌──── 订阅者 A (每人收到完整一份)
┌────────┐ 发布到 │
│ 发布者 │ ─topic──▶ ├──── 订阅者 B
└────────┘ │
└──── 订阅者 C
对比「队列」:一条消息只给 A 或 B 或 C 其中一个(分摊任务)
```
**③ 代价 / 什么时候别用**
- **代价**:① 和事件驱动一样,**全局流程难以追踪**,「这条消息到底谁收了、谁处理成功了」需要额外的可观测手段;② 订阅者多了,投递的扇出成本上升;③ 可靠性语义(至少一次?至多一次?)要想清楚,否则要么丢消息要么重复消费。
- **什么时候别用**:点对点的、一对一的明确调用,用 Pub/Sub 是杀鸡用牛刀;强一致、要立即确认的交互也不适合广播式的异步通信。
> Pub/Sub 是「事件驱动」的运输工具,「消息队列」是「异步处理」的运输工具——两者底层常是同一类中间件,区别在**投递语义**(广播 vs 分摊)。
---
## 8. 客户端-服务端 / BFF(为前端裁剪的后端)
**① 解决什么问题**
「客户端-服务端」是最基础的分工:客户端管交互和展示,服务端管数据和逻辑。而 **BFF(Backend For Frontend)** 是它的一个精致升级:**为每一种前端,单独做一个贴身的后端**。
它解决的问题是:Web、iOS、Android、智能手表……不同端的屏幕、网络、交互千差万别,它们想要的数据形状和聚合方式也不同。如果让一个通用 API 同时伺候所有端,要么接口臃肿(什么字段都给,移动端流量遭罪),要么客户端被迫自己发好几个请求东拼西凑(慢、费电)。BFF 让**每个端都有一个「专属管家」**,把后端的零散数据**裁剪、聚合成这个端最顺手的形状**。
**② 长什么样**
```
┌────────┐ ┌────────┐ ┌────────┐
│ Web │ │ iOS │ │ 第三方 │ 不同端,不同诉求
└───┬────┘ └───┬────┘ └───┬────┘
▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐
│Web BFF │ │移动 BFF │ │ 开放API │ 每个端一个「贴身后端」
└───┬────┘ └───┬────┘ └───┬────┘ 负责裁剪/聚合
└────────────┼────────────┘
▼
┌───────────────────────┐
│ 后端核心服务 / 微服务 │ 核心逻辑只写一份
└───────────────────────┘
```
**③ 代价 / 什么时候别用**
- **代价**:多了一层要开发和维护的「中间人」;如果团队不大,几个 BFF 之间容易出现重复代码;BFF 本身也可能膨胀成新的「小单体」。
- **什么时候别用**:只有一种前端、或者各端诉求高度一致时,加 BFF 纯属多此一举——一个通用 API 就够了。BFF 是「**多端 + 各端差异大 + 后端是多个服务**」时才划算。
> AI 对话产品里,「编排层」某种程度上就是一个重型 BFF:它把推理、检索、工具、会话等一堆后端能力,裁剪成「前端要的那一段流式对话」。流式输出(SSE)也是典型的客户端-服务端协作模式。见 [AI 对话产品模板](../templates/ai-chat-product/README.md)。
---
## 9. 管道-过滤器(Pipeline)
**① 解决什么问题**
把一个处理任务拆成**一串首尾相接的处理步骤(过滤器)**,数据像在流水线上一样,一站一站被加工,前一站的输出就是后一站的输入。
它解决的是「**复杂的数据加工流程,怎么拆得清晰、可复用、可独立替换**」。每个过滤器只干一件小事、只关心自己的输入输出,对上下游一无所知。这样每一步都能**独立开发、独立测试、独立替换、甚至独立扩容**;想加一个新步骤,插进管线就行。
**② 长什么样**
```
原始输入 最终产物
│
▼
┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐
│ 校验 │──▶│ 清洗 │──▶│ 转换 │──▶│ 压缩 │──▶│ 入库 │──▶
└──────┘ └──────┘ └──────┘ └──────┘ └──────┘
每个「过滤器」只管自己这一站,不知道上下游是谁
→ 任意一站都可单独替换、单独加机器
```
**③ 代价 / 什么时候别用**
- **代价**:① 整条管线的吞吐受**最慢的那一站**拖累(木桶效应);② 一站失败,要想清楚整条链路怎么回滚或重试;③ 数据在各站之间传递、序列化也有开销;④ 不适合需要「站与站之间来回交互」的逻辑——管道是单向流水,不是对话。
- **什么时候别用**:逻辑本身不是「线性流水」形态时硬套管道,会很别扭;步骤之间高度耦合、需要频繁互相回头看的场景也不适合。
> 视频转码是教科书级的管道:上传 → 切片 → 转多种码率 → 打包 → 分发 CDN,一站接一站。任何「数据加工 / ETL / 媒体处理」流程都天然是管道形态。见 [视频流媒体模板](../templates/video-streaming/README.md)。
---
## 10. 微内核 / 插件化(Microkernel / Plugin)
**① 解决什么问题**
把系统分成两部分:一个稳定的、最小的「**内核(核心)**」,加上一堆可插拔的「**插件**」。内核只提供最基础、最不变的能力,以及一套「插件怎么接进来」的规则;具体的、多变的、个性化的功能,全部做成插件。
它解决的是「**核心稳定、外围多变**」这类系统的扩展性:你希望第三方(甚至用户自己)能在不动核心代码的前提下,扩展系统能力。浏览器和它的扩展、IDE 和它的插件、各种「应用市场」生态,都是这个模式。
**② 长什么样**
```
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 插件 A │ │ 插件 B │ │ 插件 C │ ← 多变、可热插拔
└─────┬────┘ └─────┬────┘ └─────┬────┘ 可由第三方开发
│ │ │
┌─────▼────────────▼────────────▼─────┐
│ 稳 定 的 内 核 (Core) │ ← 最小、最稳定
│ 只提供基础能力 + 插件接入规则(契约) │ 很少改动
└──────────────────────────────────────┘
```
**③ 代价 / 什么时候别用**
- **代价**:① 那套「插件契约/接口」一旦定下来就**很难改**,改了所有插件都得跟着动,设计时要极其谨慎;② 插件质量参差不齐,一个烂插件可能拖垮甚至搞崩内核(所以常需要隔离/沙箱);③ 内核要预留扩展点,本身设计难度高。
- **什么时候别用**:功能稳定、没有「让外人扩展」需求的系统,搞插件化是过度设计——你只是给自己增加了一套用不上的复杂机制。
> 浏览器插件就是微内核思想的活样本:浏览器是内核,扩展在受限沙箱里运行、通过规定好的接口接入,既扩展能力又不能为所欲为。见 [浏览器插件模板](../templates/browser-extension/README.md)。
---
## 怎么选?一个简易决策提示
讲完十张牌,最怕你转头就开始「集邮」——「这个项目我要把模式都用上」。**打住。** 选模式只需要顺着问几个问题:
```
┌─────────────────────────────────────────┐
│ Step 0:能不用模式就先别用 │
│ 最简单的「分层单体」能不能解决?能 → 就这样 │
└────────────────────┬────────────────────┘
│ 解决不了,才往下问
▼
┌──────────────────────────────────────────────────────────┐
│ 问 1:我遇到的到底是什么问题?(对症,别对「时髦」) │
│ • 代码搅成一团 ───────────────▶ 分层 / 模块化 │
│ • 一件事要扇出通知很多方 ───────▶ 事件驱动 / Pub/Sub │
│ • 有耗时任务 / 要削峰 ──────────▶ 消息队列 / 异步 │
│ • 读写诉求严重不对称 ───────────▶ CQRS │
│ • 多端、各端形状差异大 ─────────▶ BFF │
│ • 线性数据加工流程 ─────────────▶ 管道-过滤器 │
│ • 核心稳定、要让外人扩展 ───────▶ 微内核 / 插件 │
│ • 多团队互相阻塞、组织扛不住 ────▶ (慎重)微服务 │
└────────────────────────────────┬─────────────────────────┘
▼
┌──────────────────────────────────────────────────────────┐
│ 问 2:这个模式的「代价」我现在付得起、也愿意付吗? │
│ 付不起 / 问题还没真出现 ──▶ 先别上,记一笔「以后可能要」 │
└──────────────────────────────────────────────────────────┘
```
三条心法,送给你:
1. **从简单开始,被痛驱动地演进。** 不要预支未来的复杂度。等问题真的出现、且痛在明确的某处,再引入对应模式。
2. **模式可以组合。** 真实系统几乎都是「分层单体」打底,局部用上事件驱动、异步、缓存。模式不是单选题,而是「在合适的局部用合适的牌」。
3. **永远能说出代价。** 你引入任何一个模式,如果说不清「它换走了我的什么」(简单性?一致性?可观测性?),那你大概率是在跟风,不是在做架构。
---
## 📌 真实案例:这些模式,谁在用
模式不是教科书概念,每一个背后都有真实系统在用:
- **单体 / 模块化单体** → [Shopify](https://shopify.engineering/shopify-monolith)(280 万行 Ruby)、[Stack Overflow](https://nickcraver.com/blog/2016/02/17/stack-overflow-the-architecture-2016-edition/)、[Basecamp/DHH](https://medium.com/signal-v-noise/the-majestic-monolith-29166d022228)
- **微服务**(及其务实回退)→ Netflix 是标杆;但 [Amazon Prime Video](https://thenewstack.io/return-of-the-monolith-amazon-dumps-microservices-for-video-monitoring/) 和 [Segment](https://www.twilio.com/en-us/blog/developers/best-practices/goodbye-microservices) 都从微服务**回到了单体**
- **事件驱动 / 消息队列** → [电商模板](../templates/ecommerce-platform/README.md) 的下单扇出、[通知系统](../templates/notification-system/README.md)
- **CQRS / 读写分离** → [社交信息流](../templates/social-feed/README.md)、[短链接](../templates/url-shortener/README.md)
- **管道 - 过滤器** → [视频转码](../templates/video-streaming/README.md);**微内核 / 插件** → [浏览器插件](../templates/browser-extension/README.md)
> 「该用哪个、谁又用错了」的真实对比,见 [09 · 架构品味](09-架构品味.md)。
---
## 本章小结
- **模式 = 对反复出现的问题的成熟解法。** 学它是为了「手里有牌」和「沟通有共同语言」,不是为了显得高级。
- 十张牌各有各的「适用问题」和「代价」:**分层**切关注点、**单体**是被低估的正确起点、**微服务**是被滥用的成熟期解药、**事件驱动 / Pub/Sub** 做解耦与扇出、**消息队列 / 异步**做削峰与解耦、**CQRS** 应对读写不对称、**BFF** 服务多端、**管道**做线性加工、**微内核**做可扩展生态。
- **最重要的两句话**:① **模式是工具不是目标,别为了用而用**;② **微服务被严重滥用——它首先解决「人」的扩展,不是「机器」的;在组织规模、独立部署需求、平台能力三个前提没满足前,默认用模块化单体。**
- 选模式的口诀:**能不用就先别用 → 对症下药 → 付得起代价才上**。
## 🎯 随堂检验
学完这十张牌,做几道题检验一下(点选项即时看对错):
> **承上启下**:这十个模式里,凡是涉及「一致性」「最终一致」「事务」「数据放哪」的地方(微服务、事件驱动、CQRS……),背后都指向同一个系统中最硬的骨头——**数据与状态**。逻辑好改,数据难改。下一章 [05 · 数据与状态](05-数据与状态.md),我们就去啃这块真正的难点。