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