Skip to content

04 · 十大核心架构模式

模式不是用来炫技的咒语,而是前人替你踩过坑后留下的「成熟解法」。学模式,是为了手里有牌——遇到问题时,脑子里能立刻浮现几种打法,而不是从零硬想。


先说清楚:什么是「架构模式」,以及为什么要学它

你写代码会遇到「这段逻辑似曾相识」的感觉;架构也一样。同一类问题,在无数系统里反复出现:怎么把复杂度切开?怎么让系统在流量洪峰下不被冲垮?怎么让读和写互不拖累?

每当一个问题被足够多的人、用足够多的方式解决过,其中那些反复被证明有效的解法,就沉淀成了「模式」。模式是被验证过的套路,是社区的共识词汇。

学模式有两层价值:

  1. 手里有牌:遇到问题不至于裸奔。你知道「哦,这是个典型的读写分离场景」,而不是对着白板发呆。
  2. 沟通带宽:你对同事说「这里走事件驱动」,他立刻懂你指的是哪一套结构、有哪些含义。模式是架构师之间的「行话」,一个词省下半小时解释。

但请把下面这句话刻在脑子里,它是本章的灵魂:

模式是工具,不是目标。 没有「高级模式」和「低级模式」之分,只有「合不合适」之分。 一个用对了的单体,远比一个用错了的微服务高级。

下面逐个过这十张「牌」。每张牌我都会讲三件事:①它解决什么问题 ②它长什么样 ③它的代价 / 什么时候千万别用。第三点最重要——看不到代价,就等于没学会这个模式


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:这个模式的「代价」我现在付得起、也愿意付吗?            │
   │    付不起 / 问题还没真出现 ──▶ 先别上,记一笔「以后可能要」     │
   └──────────────────────────────────────────────────────────┘

三条心法,送给你:

  1. 从简单开始,被痛驱动地演进。 不要预支未来的复杂度。等问题真的出现、且痛在明确的某处,再引入对应模式。
  2. 模式可以组合。 真实系统几乎都是「分层单体」打底,局部用上事件驱动、异步、缓存。模式不是单选题,而是「在合适的局部用合适的牌」。
  3. 永远能说出代价。 你引入任何一个模式,如果说不清「它换走了我的什么」(简单性?一致性?可观测性?),那你大概率是在跟风,不是在做架构。

📌 真实案例:这些模式,谁在用

模式不是教科书概念,每一个背后都有真实系统在用:

「该用哪个、谁又用错了」的真实对比,见 09 · 架构品味


本章小结

  • 模式 = 对反复出现的问题的成熟解法。 学它是为了「手里有牌」和「沟通有共同语言」,不是为了显得高级。
  • 十张牌各有各的「适用问题」和「代价」:分层切关注点、单体是被低估的正确起点、微服务是被滥用的成熟期解药、事件驱动 / Pub/Sub 做解耦与扇出、消息队列 / 异步做削峰与解耦、CQRS 应对读写不对称、BFF 服务多端、管道做线性加工、微内核做可扩展生态。
  • 最重要的两句话:① 模式是工具不是目标,别为了用而用;② 微服务被严重滥用——它首先解决「人」的扩展,不是「机器」的;在组织规模、独立部署需求、平台能力三个前提没满足前,默认用模块化单体。
  • 选模式的口诀:能不用就先别用 → 对症下药 → 付得起代价才上

🎯 随堂检验

学完这十张牌,做几道题检验一下(点选项即时看对错):

🤔微服务首先解决的到底是什么问题?
  • A机器性能不够、扛不住流量
  • B团队 / 组织协作的扩展性(多团队互相阻塞、谁也不敢动谁的代码)
  • C让代码看起来更优雅、更先进
🤔读远多于写、且读和写的诉求差异巨大时,适合考虑哪个模式?
  • A微内核 / 插件化
  • BCQRS(读写模型分离)
  • C管道 - 过滤器
⚖️组件间通信:用同步调用,还是事件驱动?
同步调用
你直接命令对方「去做 X」并等它做完。流程清晰、能立刻拿到结果、好调试。
VS
事件驱动
你只广播「发生了 X」,谁关心谁自己响应。高度解耦、易扩展,但整体流程「看不见」,要处理重复 / 乱序 / 最终一致。

承上启下:这十个模式里,凡是涉及「一致性」「最终一致」「事务」「数据放哪」的地方(微服务、事件驱动、CQRS……),背后都指向同一个系统中最硬的骨头——数据与状态。逻辑好改,数据难改。下一章 05 · 数据与状态,我们就去啃这块真正的难点。