<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>软件架构 on 方糖猫Blog</title>
    <link>https://ftmao.com/blog/tags/%E8%BD%AF%E4%BB%B6%E6%9E%B6%E6%9E%84/</link>
    <description>AI 视频、AI Agent 与工程实践笔记</description>
    <generator>Hugo</generator>
    <language>zh-CN</language>
    <lastBuildDate>Sat, 26 Oct 2024 14:00:00 +0800</lastBuildDate><atom:link href="https://ftmao.com/blog/tags/%E8%BD%AF%E4%BB%B6%E6%9E%B6%E6%9E%84/index.xml" rel="self" type="application/rss+xml" /><item>
      <title>《架构整洁之道》详解：从编程范式到整洁架构的完整指南</title>
      <link>https://ftmao.com/blog/bookstore/clean-architecture-book-notes/</link>
      <pubDate>Sat, 26 Oct 2024 14:00:00 +0800</pubDate>
      <guid>https://ftmao.com/blog/bookstore/clean-architecture-book-notes/</guid>
      <description>&lt;h1 id=&#34;架构整洁之道详解从编程范式到整洁架构的完整指南&#34;&gt;《架构整洁之道》详解：从编程范式到整洁架构的完整指南&lt;/h1&gt;
&lt;p&gt;《架构整洁之道》（Clean Architecture: A Craftsman&amp;rsquo;s Guide to Software Structure and Design）是 Robert C. Martin（业内昵称&amp;quot;Bob 大叔&amp;quot;）在 2017 年出版的一本书。它是《代码整洁之道》《敏捷软件开发》之后，Bob 大叔对自己半个世纪编程经验的又一次系统总结。&lt;/p&gt;
&lt;p&gt;这本书要回答的核心问题只有一个：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;如何设计一个系统，让它活得久、改得动、换得起？&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;本文面向&lt;strong&gt;从未接触过软件架构概念&lt;/strong&gt;的读者，按照全书六个部分的结构逐层展开，配合图示和实例，帮你建立完整的知识地图。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第一部分-概述架构到底是什么&#34;&gt;第一部分 概述：架构到底是什么&lt;/h2&gt;
&lt;h3 id=&#34;1-设计与架构没有本质区别&#34;&gt;1. 设计与架构没有本质区别&lt;/h3&gt;
&lt;p&gt;很多人以为&amp;quot;架构&amp;quot;是宏观的大图，&amp;ldquo;设计&amp;quot;是微观的细节。Bob 大叔认为两者其实是一回事：&lt;strong&gt;架构就是系统设计，系统设计就是架构&lt;/strong&gt;。它们只是在不同的观察尺度上，不存在一条清晰的分界线，两者共同决定了系统的整体形状。&lt;/p&gt;
&lt;p&gt;一个好的架构目标可以用一句话概括：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;用最小的人力成本来满足构建和维护该系统的需求。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;衡量标准很朴素：如果每次新增需求所需的成本都维持在较低水平，不会因为系统变老而持续上升，那这个架构就是好架构。反之，如果每改一个功能都要牵动全身、修复一个 bug 引入三个新 bug，那架构就已经坏掉了。&lt;/p&gt;
&lt;h3 id=&#34;2-两个价值维度行为与架构&#34;&gt;2. 两个价值维度：行为与架构&lt;/h3&gt;
&lt;p&gt;每个软件系统都为利益相关者提供两种不同的价值：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;行为价值（Behavior）&lt;/strong&gt;：系统按需求文档干活，帮用户赚钱或省钱。这是程序员最关注的部分。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;架构价值（Architecture）&lt;/strong&gt;：软件之所以叫&amp;quot;软&amp;quot;件，就在于它&lt;strong&gt;应该是容易被修改的&lt;/strong&gt;。如果需求变了而系统改不动，这个系统就失败了。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这两者的优先级，可以用艾森豪威尔矩阵（重要/紧急四象限）来思考：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;&lt;/th&gt;
					&lt;th&gt;紧急&lt;/th&gt;
					&lt;th&gt;不紧急&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;重要&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;① 行为（功能）&lt;/td&gt;
					&lt;td&gt;② &lt;strong&gt;架构&lt;/strong&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;不重要&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;③ 界面细节等&lt;/td&gt;
					&lt;td&gt;④ 杂项&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;常见的悲剧是：业务经理永远把&amp;quot;紧急&amp;quot;排在前面，于是团队不断牺牲&amp;quot;重要但不紧急&amp;quot;的架构，最终系统腐化到无法维护。&lt;strong&gt;为好的架构而斗争，是软件架构师的职责&lt;/strong&gt;——因为架构师本身就是利益相关者之一。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第二部分-从基础构件开始编程范式&#34;&gt;第二部分 从基础构件开始：编程范式&lt;/h2&gt;
&lt;p&gt;这一部分回顾了软件历史上仅有的三种编程范式。一个很有意思的观点是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这三种范式都是 1958–1968 这十年间被发明的，之后的几十年里，&lt;strong&gt;再也没有出现过新的编程范式&lt;/strong&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;三种范式它们都在限制我们&#34;&gt;三种范式：它们都在&amp;quot;限制&amp;quot;我们&lt;/h3&gt;
&lt;p&gt;三种范式有一个共性：它们都&lt;strong&gt;剥夺&lt;/strong&gt;了程序员的某种能力，而不是赋予新能力。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    A[结构化编程&amp;lt;br/&amp;gt;1968 Dijkstra] --&amp;gt;|限制了| A1[goto 的无约束跳转]
    B[面向对象编程&amp;lt;br/&amp;gt;1966 Simula] --&amp;gt;|限制了| B1[函数指针的滥用&amp;lt;br/&amp;gt;通过多态接管间接调用]
    C[函数式编程&amp;lt;br/&amp;gt;1958 λ演算/Lisp] --&amp;gt;|限制了| C1[赋值语句&amp;lt;br/&amp;gt;变量不可变性]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;结构化编程&lt;/strong&gt;（Structured Programming）：用顺序、选择、循环三种结构取代了自由的 &lt;code&gt;goto&lt;/code&gt; 跳转。Dijkstra 的名言是&amp;quot;goto 是有害的&amp;rdquo;。这一范式还带来一个深远影响：既然无法数学式地&amp;quot;证明&amp;quot;程序正确，那就通过&lt;strong&gt;测试&lt;/strong&gt;来&amp;quot;证伪&amp;quot;——结构化编程催生了测试驱动开发（TDD）的思想土壤。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;面向对象编程&lt;/strong&gt;（OOP）：把数据和函数打包成对象。关于 OOP 的三大特性，书里有独到见解：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;封装&lt;/strong&gt;：其实 C 语言用头文件隐藏实现做得更好，OOP 反而弱化了它；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;继承&lt;/strong&gt;：C 程序员早就手动模拟过，OOP 只是让它更方便；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;多态&lt;/strong&gt;：这才是 OOP 真正的核心——它让&lt;strong&gt;源码依赖方向可以被反转&lt;/strong&gt;。调用方通过接口调用实现方，源码依赖与控制流方向相反，这就是全书后面反复强调的&amp;quot;依赖反转&amp;quot;的武器。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;函数式编程&lt;/strong&gt;（Functional Programming）：核心思想是&lt;strong&gt;变量不可变&lt;/strong&gt;（immutability）。没有赋值，就没有竞态条件、死锁、并发更新问题——所有并发 bug 都源于可变状态。现实中的妥协方案是&amp;quot;事件溯源&amp;quot;（Event Sourcing）：只存事务日志，不存状态，需要状态时用日志重算，从而消灭对可变状态的依赖。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;给初学者的 takeaway&lt;/strong&gt;：软件架构的底层构件只有三种——顺序、分支、循环；而范式教会我们的是&amp;quot;&lt;strong&gt;不要做什么&lt;/strong&gt;&amp;quot;，架构师要在这些限制内搭建系统。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第三部分-设计原则solid&#34;&gt;第三部分 设计原则：SOLID&lt;/h2&gt;
&lt;p&gt;SOLID 是函数/类级别（中层）的五个设计原则，目标是让软件模块：&lt;strong&gt;可扩展、易修改、易理解、可复用&lt;/strong&gt;。这部分是全书中读者基础最扎实的部分，但书里对每条原则的理解都比市面上的通俗版本更精确。&lt;/p&gt;
&lt;h3 id=&#34;srp-单一职责原则&#34;&gt;SRP 单一职责原则&lt;/h3&gt;
&lt;p&gt;最容易被误解的一条。SRP &lt;strong&gt;不是&lt;/strong&gt;&amp;ldquo;每个函数只做一件事&amp;rdquo;（那是函数层面的小原则），而是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;一个模块应该只对一类行为者（actor）负责。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;经典例子：一个 &lt;code&gt;Employee&lt;/code&gt; 类同时有 &lt;code&gt;calculatePay()&lt;/code&gt;（CFO 关心）、&lt;code&gt;reportHours()&lt;/code&gt;（COO 关心）、&lt;code&gt;save()&lt;/code&gt;（CTO 关心）三个方法。三个方法服务三类不同的行为者，一旦为 CFO 改的代码意外影响了 COO 的报表，就是 SRP 被破坏的恶果。解法是按行为者拆分。&lt;/p&gt;
&lt;h3 id=&#34;ocp-开闭原则&#34;&gt;OCP 开闭原则&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;对扩展开放，对修改关闭。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;新增行为应该通过增加代码完成，而不是修改旧代码。实现手段是：把系统按&amp;quot;变更原因&amp;quot;分层，高层策略组件不受低层细节变化的影响，层与层之间用接口隔离。&lt;/p&gt;
&lt;h3 id=&#34;lsp-里氏替换原则&#34;&gt;LSP 里氏替换原则&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;任何使用基类的地方，替换成子类后行为依然正确。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;违反 LSP 的著名案例：正方形（Square）继承长方形（Rectangle）——&lt;code&gt;setWidth&lt;/code&gt; 语义在两个类里不一致，导致依赖长方形的代码出问题。在架构层面，LSP 延伸为&amp;quot;接口/服务的可替换性&amp;quot;：REST 接口协议不一致，本质上也是违反 LSP。&lt;/p&gt;
&lt;h3 id=&#34;isp-接口隔离原则&#34;&gt;ISP 接口隔离原则&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;不要强迫使用者依赖它们不需要的方法。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;胖接口会引入不必要的依赖——改动接口中别人不用的部分，也会强制所有使用者重新编译部署。架构层面的教训：&lt;strong&gt;任何层次的&amp;quot;多余依赖&amp;quot;都有害&lt;/strong&gt;，包括对一个打包了过多功能的库或框架的依赖。&lt;/p&gt;
&lt;h3 id=&#34;dip-依赖反转原则&#34;&gt;DIP 依赖反转原则&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;源码依赖方向应与控制流方向相反，高层策略不应依赖低层细节，两者都应依赖抽象。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这是整本书的&amp;quot;元原则&amp;quot;。Java 这类语言中，接口声明属于调用方一侧（内层），实现类属于被调用方一侧（外层），依赖箭头跨过边界指向内层——这就是后面整洁架构能成立的技术基础。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    subgraph 内层[&amp;#34;内层（高层策略）&amp;#34;]
        UC[用例] --&amp;gt;|依赖接口| PORT[&amp;#34;接口（输出端口）&amp;#34;]
    end
    subgraph 外层[&amp;#34;外层（实现细节）&amp;#34;]
        IMPL[Presenter 实现] -.实现.-&amp;gt; PORT
        DB[数据库实现] -.实现.-&amp;gt; PORT2[数据访问接口]
    end
    UC --&amp;gt;|依赖接口| PORT2
    style 内层 fill:#e8f0fe
    style 外层 fill:#fef3e8&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;第四部分-组件构建原则&#34;&gt;第四部分 组件构建原则&lt;/h2&gt;
&lt;p&gt;SOLID 管&amp;quot;类和函数怎么写&amp;quot;，这一部分管&amp;quot;&lt;strong&gt;类和函数怎么打包成组件&lt;/strong&gt;&amp;quot;（组件 = 独立部署的最小单元，如 jar、dll、服务）。&lt;/p&gt;
&lt;h3 id=&#34;组件聚合三原则哪些类该放进一个组件&#34;&gt;组件聚合三原则（哪些类该放进一个组件）&lt;/h3&gt;
&lt;p&gt;三条原则互相拉扯，架构师要做权衡：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;REP 复用/发布等同原则&lt;/strong&gt;：组件的复用粒度等于发布粒度——一个组件里的东西应该作为一个整体被版本化和发布。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CCP 共同闭包原则&lt;/strong&gt;：会因同一原因、同一时间变化的类应该聚在同一个组件里（SRP 在组件层面的放大版）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CRP 共同复用原则&lt;/strong&gt;：不要强迫组件的使用者依赖他们不需要的东西（ISP 在组件层面的放大版）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;CCP 和 CRP 通常是矛盾的：按变更原因聚合容易，但组件可能变胖；按使用方切得细，组件数量又会爆炸。&lt;/p&gt;
&lt;h3 id=&#34;组件耦合三原则组件之间怎么依赖&#34;&gt;组件耦合三原则（组件之间怎么依赖）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ADP 无环依赖原则&lt;/strong&gt;：组件依赖图中不允许出现环。打破环的手段有两个：依赖反转（在中间加接口）或新建一个公共组件。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SDP 稳定依赖原则&lt;/strong&gt;：依赖方向必须指向更稳定的组件。越难修改的组件越稳定，不稳定（易变）的组件应该依赖稳定的组件。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SAP 稳定抽象原则&lt;/strong&gt;：越稳定的组件应该越抽象。所以架构应该是：&lt;strong&gt;核心策略 = 稳定 + 抽象（接口）&lt;/strong&gt;，外围实现 = 不稳定 + 具体。极端的两端是&amp;quot;最稳定且最抽象&amp;quot;（理想）和&amp;quot;最不稳定且最具体&amp;quot;（胶水代码），大部分组件落在两者之间的主序列附近。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第五部分-软件架构本书的核心&#34;&gt;第五部分 软件架构：本书的核心&lt;/h2&gt;
&lt;h3 id=&#34;1-什么是软件架构&#34;&gt;1. 什么是软件架构&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;架构的实质是：&lt;strong&gt;尽可能长时间地推迟决策，保留尽可能多的可选项。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;一个软件系统的架构质量，取决于它&lt;strong&gt;支撑系统全生命周期&lt;/strong&gt;的能力：开发、部署、运行、维护。好的架构让系统易于理解、易于开发、易于维护、易于部署，其终极目标是&lt;strong&gt;最小化整个生命周期的人力成本&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;架构的次要目标还包括让系统可以按用例独立开发、独立部署、独立运行。&lt;/p&gt;
&lt;h3 id=&#34;2-独立性&#34;&gt;2. 独立性&lt;/h3&gt;
&lt;p&gt;好架构必须支持：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;按用例解耦&lt;/strong&gt;：购物车、订单、用户管理……不同用例之间的代码互不干扰；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;解耦运行方式&lt;/strong&gt;：系统可以跑成单体、SOA、微服务，部署方式是可推迟的决策；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;独立开发与独立部署&lt;/strong&gt;：不同团队可以并行开发不同模块而互不阻塞。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;3-划分边界软件架构就是画线的艺术&#34;&gt;3. 划分边界：软件架构就是画线的艺术&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;架构师的工作是&lt;strong&gt;画出边界线&lt;/strong&gt;，把系统分割成组件。一部分组件实现核心业务逻辑，另一部分组件作为与核心逻辑无关的&amp;quot;插件&amp;quot;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;划线的依据是：&lt;strong&gt;哪些东西变化的原因不同&lt;/strong&gt;。UI、数据库、业务规则、第三方服务，它们变化的理由完全不同，所以它们之间应该画线。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TD
    UI[用户界面] -.插件式依赖.-&amp;gt; CORE
    DB[(数据库)] -.插件式依赖.-&amp;gt; CORE
    WEB[Web 框架] -.插件式依赖.-&amp;gt; CORE
    EXT[外部服务] -.插件式依赖.-&amp;gt; CORE
    subgraph CORE[&amp;#34;核心业务逻辑（不知道外围的存在）&amp;#34;]
        ENT[业务实体]
        UC[用例]
    end
    style CORE fill:#e8f0fe&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;关键点：&lt;strong&gt;核心业务逻辑根本不依赖这些东西，是这些东西反过来依赖核心业务逻辑&lt;/strong&gt;。数据库不是系统的中心，业务规则才是。这种&amp;quot;以核心业务为中心，外设皆插件&amp;quot;的思想，Bob 大叔称之为&lt;strong&gt;插件式架构&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id=&#34;4-边界剖析跨边界的调用怎么发生&#34;&gt;4. 边界剖析：跨边界的调用怎么发生&lt;/h3&gt;
&lt;p&gt;跨越边界的通信分两种方向：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;顺控制流调用&lt;/strong&gt;（外层主动调用内层）：直接调用即可，依赖方向天然向内，没有架构问题；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;逆控制流回调&lt;/strong&gt;（内层需要通知外层）：必须用&lt;strong&gt;依赖反转&lt;/strong&gt;——内层定义接口，外层实现接口，保证源码依赖依然指向内层。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;5-策略与层次&#34;&gt;5. 策略与层次&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;软件系统就是一种对&amp;quot;策略（policy）&amp;ldquo;的陈述：详细描述如何根据输入计算输出。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;架构要做的就是把策略按&lt;strong&gt;层次&lt;/strong&gt;分开：越靠近输入输出的策略层次越低（格式化、校验、IO），越远离的策略层次越高（业务规则）。高层策略与低层细节分离，低层策略应该成为高层策略的&amp;quot;插件&amp;rdquo;。&lt;/p&gt;
&lt;h3 id=&#34;6-业务逻辑实体与用例&#34;&gt;6. 业务逻辑：实体与用例&lt;/h3&gt;
&lt;p&gt;书里把业务逻辑分成两类：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;业务实体（Entity / 关键业务逻辑）&lt;/strong&gt;：与具体应用无关的、企业级最核心的规则。比如&amp;quot;贷款利率 = 基准利率 + 风险系数&amp;quot;，不管系统做成 Web、APP 还是命令行，这条规则都不变。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;用例（Use Case / 应用相关逻辑）&lt;/strong&gt;：描述某个具体应用如何使用实体。比如&amp;quot;贷款审批流程：先查征信 → 再评估额度 → 最后出审批结果&amp;quot;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;用例依赖实体，实体不知道用例的存在。&lt;strong&gt;实体是最通用、最高层、最不容易变的；用例次之。&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&#34;7-尖叫的软件架构&#34;&gt;7. 尖叫的软件架构&lt;/h3&gt;
&lt;p&gt;这一章名字很有趣：&lt;strong&gt;你的架构应该&amp;quot;尖叫&amp;quot;着说出自己是做什么的&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;看一个系统的目录结构，如果看到的是 &lt;code&gt;models / views / controllers&lt;/code&gt;，这是框架在尖叫；如果看到的是 &lt;code&gt;order / payment / shipping&lt;/code&gt;（订单、支付、物流），这是业务在尖叫。好的架构应该一眼看出这是一个&amp;quot;图书管理系统&amp;quot;或&amp;quot;电商系统&amp;quot;，而不是&amp;quot;一个 Spring MVC 项目&amp;quot;。框架是工具，不应该喧宾夺主。&lt;/p&gt;
&lt;h3 id=&#34;8-整洁架构四层同心圆全书最著名的一张图&#34;&gt;8. 整洁架构：四层同心圆（全书最著名的一张图）&lt;/h3&gt;
&lt;p&gt;Bob 大叔综合了三种前辈架构思想——六边形架构（Hexagonal / Ports and Adapters）、DCI、BCE——提炼出统一的模型：&lt;strong&gt;整洁架构（The Clean Architecture）&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;这些架构的共同特点是系统：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;独立于框架&lt;/strong&gt;：框架是工具，不是牢笼；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可被测试&lt;/strong&gt;：业务逻辑可以脱离 UI、数据库、Web 服务测试；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;独立于 UI&lt;/strong&gt;：Web 界面换成命令行界面，业务逻辑一行不用改；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;独立于数据库&lt;/strong&gt;：Oracle 换成 MongoDB，业务逻辑不知道；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;独立于任何外部机构&lt;/strong&gt;：业务逻辑对外部接口一无所知。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;用 Mermaid 重绘经典的同心圆图（越靠内层，策略越高层、越抽象）：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    subgraph L4[&amp;#34;🟠 框架与驱动层 Frameworks &amp;amp; Drivers（最外层：实现细节）&amp;#34;]
        direction LR
        FW[Web 框架]
        DB[(数据库)]
        DEV[设备 / 外部接口]
    end
    subgraph L3[&amp;#34;🟡 接口适配层 Interface Adapters&amp;#34;]
        direction LR
        CTRL[Controllers]
        PRE[Presenters]
        GATE[Gateways]
    end
    subgraph L2[&amp;#34;🟢 用例层 Use Cases（应用业务逻辑）&amp;#34;]
        UC[用例交互器&amp;lt;br/&amp;gt;Use Case Interactor]
    end
    subgraph L1[&amp;#34;🔵 业务实体层 Entities（企业核心业务逻辑，最内层）&amp;#34;]
        ENT[业务实体]
    end
    L4 --&amp;gt;|依赖指向内层| L3
    L3 --&amp;gt;|依赖指向内层| L2
    L2 --&amp;gt;|依赖指向内层| L1&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;贯穿整个架构的只有一条&lt;strong&gt;依赖关系规则（The Dependency Rule）&lt;/strong&gt;：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;源码中的依赖关系必须只指向同心圆的内层，即由低层机制指向高层策略。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;内层代码不能引用外层代码中声明的任何名字——函数、类、变量，一概不行。外层使用的数据格式（尤其是框架生成的）也不能被内层使用。&lt;/p&gt;
&lt;p&gt;各层职责：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;业务实体（Entities）&lt;/strong&gt;：封装全系统最关键、最通用的业务逻辑，是整个系统中最不容易受外界变化影响的部分。页面导航改版、安全策略调整，都不应该触及实体层。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;用例（Use Cases）&lt;/strong&gt;：封装特定应用的业务逻辑，指挥数据在实体间流动，调度实体完成用例目标。用例的变化不影响实体，数据库/UI 的变化也不影响用例。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;接口适配器（Interface Adapters）&lt;/strong&gt;：一组数据格式转换器。GUI 的整个 MVC 都在这一层：Controllers、Presenters、Views。所有 SQL 语句也应该被限制在这一层。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;框架与驱动（Frameworks &amp;amp; Drivers）&lt;/strong&gt;：最外层，只写与内层沟通的胶水代码。Web 是细节，数据库是细节，都放在外层，&amp;ldquo;让它们伤不到内层&amp;rdquo;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：四层不是硬性规定，真正的系统可以多于四层，但依赖关系规则永远成立。&lt;/p&gt;
&lt;h3 id=&#34;9-一个完整的请求流程web--数据库的典型场景&#34;&gt;9. 一个完整的请求流程（Web + 数据库的典型场景）&lt;/h3&gt;
&lt;p&gt;书中最实用的一张图描述了一个请求如何穿越各层。用&amp;quot;用户下单&amp;quot;为例：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;sequenceDiagram
    participant U as 用户
    participant C as Controller&amp;lt;br/&amp;gt;(接口适配层)
    participant IB as InputBoundary&amp;lt;br/&amp;gt;(输入端口接口, 用例层)
    participant UCI as UseCaseInteractor&amp;lt;br/&amp;gt;(用例层)
    participant E as Entities&amp;lt;br/&amp;gt;(实体层)
    participant DAI as DataAccessInterface&amp;lt;br/&amp;gt;(数据访问接口, 用例层)
    participant DB as 数据库实现&amp;lt;br/&amp;gt;(最外层)
    participant OB as OutputBoundary&amp;lt;br/&amp;gt;(输出端口接口, 用例层)
    participant P as Presenter&amp;lt;br/&amp;gt;(接口适配层)
    participant V as View&amp;lt;br/&amp;gt;(最外层)

    U-&amp;gt;&amp;gt;C: 提交订单(HTTP 请求)
    C-&amp;gt;&amp;gt;IB: 包装成简单数据对象, 跨边界传入
    IB-&amp;gt;&amp;gt;UCI: 调用
    UCI-&amp;gt;&amp;gt;E: 调度实体执行业务规则
    UCI-&amp;gt;&amp;gt;DAI: 通过接口请求数据
    DAI-&amp;gt;&amp;gt;DB: 外层实现执行 SQL
    DB--&amp;gt;&amp;gt;UCI: 返回简单数据结构
    UCI-&amp;gt;&amp;gt;E: 收集结果
    UCI-&amp;gt;&amp;gt;OB: 组装 OutputData, 通过输出端口传出
    OB-&amp;gt;&amp;gt;P: Presenter 实现该接口
    P-&amp;gt;&amp;gt;V: 格式化成 ViewModel(纯字符串/开关)
    V--&amp;gt;&amp;gt;U: 渲染 HTML&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;三个要点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;控制流&lt;/strong&gt;从 Controller 出发，穿过用例，最终到达 Presenter——方向是从外到内再到外；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;源码依赖&lt;/strong&gt;每一步都指向内层：Controller 依赖 InputBoundary 接口、Presenter 实现 OutputBoundary 接口，两者都属于&amp;quot;外层依赖内层&amp;quot;；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;跨边界的数据&lt;/strong&gt;永远是&lt;strong&gt;简单的数据结构&lt;/strong&gt;（DTO / 函数参数 / POJO），绝不直接传递业务实体对象或数据库行结构——那会偷偷引入指向外层的依赖。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Presenter 的工作也很值得注意：它把 &lt;code&gt;OutputData&lt;/code&gt; 中的 &lt;code&gt;Date&lt;/code&gt;、&lt;code&gt;Currency&lt;/code&gt; 对象&lt;strong&gt;预先格式化&lt;/strong&gt;成字符串，把按钮名称、是否置灰的开关都放进 &lt;code&gt;ViewModel&lt;/code&gt;。于是 View 只剩下一件机械的事：把 ViewModel 搬进 HTML。&lt;/p&gt;
&lt;h3 id=&#34;10-展示器与谦卑对象&#34;&gt;10. 展示器与谦卑对象&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;谦卑对象（Humble Object）模式&lt;/strong&gt;是贯穿架构边界的通用模式：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;难以测试的代码（GUI、数据库网关、外部服务客户端）应该被压缩成尽量薄的&amp;quot;谦卑对象&amp;quot;，把可测试的逻辑抽到旁边的对象里。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;GUI：View 是谦卑对象（只搬数据，不测试）；Presenter 是可测试对象（负责格式化、决定按钮置灰）。&lt;/li&gt;
&lt;li&gt;数据库网关：SQL/ORM 部分是谦卑对象；用例与网关之间的接口实现是逻辑所在。&lt;/li&gt;
&lt;li&gt;服务网关：类似处理。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;11-不完全边界架构的成本控制&#34;&gt;11. 不完全边界：架构的成本控制&lt;/h3&gt;
&lt;p&gt;完整边界（双向接口 + 依赖反转）的构建和维护成本不低。敏捷社区会拿 YAGNI（&amp;ldquo;你不会需要它&amp;rdquo;）来反对预防性设计，但架构师的本职就是做预见。折中方案是&lt;strong&gt;不完全边界（Partial Boundary）&lt;/strong&gt;，书给了三种：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TD
    subgraph 方案一[&amp;#34;方案一：省掉最后一步&amp;#34;]
        C1[Client] --&amp;gt; SB1[组件A 与组件B&amp;lt;br/&amp;gt;已按完整边界设计好接口与数据格式&amp;lt;br/&amp;gt;但合并为单个组件编译部署]
    end
    subgraph 方案二[&amp;#34;方案二：单向边界（策略模式）&amp;#34;]
        C2[Client] --&amp;gt; IF[ServiceBoundary 接口]
        IMPL2[ServiceImpl] -.实现.-&amp;gt; IF
    end
    subgraph 方案三[&amp;#34;方案三：门户模式 Facade（最简）&amp;#34;]
        C3[Client] --&amp;gt; F[Facade 类&amp;lt;br/&amp;gt;只做调用转发]
        F --&amp;gt; S1[服务函数1]
        F --&amp;gt; S2[服务函数2]
    end&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;三种方式成本递减、保护强度也递减。实践启示：&lt;strong&gt;哪怕预算有限，也要至少用单向接口把边界的位置&amp;quot;占住&amp;quot;&lt;/strong&gt;，给将来升级成完整边界留好路。&lt;/p&gt;
&lt;h3 id=&#34;12-main-组件系统的脏秘密&#34;&gt;12. Main 组件：系统的&amp;quot;脏秘密&amp;quot;&lt;/h3&gt;
&lt;p&gt;每个系统都有一个&lt;strong&gt;最底层的细节：入口 Main 组件&lt;/strong&gt;。它是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;初始细节的所在地：负责创建所有工厂、策略、全局设施；&lt;/li&gt;
&lt;li&gt;最大的&amp;quot;具体&amp;quot;组件：它知道所有依赖关系，负责用依赖注入把它们组装起来；&lt;/li&gt;
&lt;li&gt;系统中&lt;strong&gt;唯一允许&amp;quot;知道一切&amp;quot;的组件&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;可以把它理解为插件系统的总装车间：所有插件都插到 Main 上，而插件们彼此互不相识。Spring 的 ApplicationContext 初始化代码，本质上就是 Main 组件。&lt;/p&gt;
&lt;h3 id=&#34;13-服务与测试&#34;&gt;13. 服务与测试&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;服务化（SOA/微服务）的收益被高估了&lt;/strong&gt;：服务之间的解耦往往不彻底（共享数据、共享网络），服务边界并不自动等于架构边界。架构边界由&amp;quot;依赖方向&amp;quot;决定，而不是由&amp;quot;是否拆成服务&amp;quot;决定。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;测试也是系统的一部分&lt;/strong&gt;：测试代码应该是系统中最独立的组件——通过测试 API 调用业务逻辑，而不是通过 UI 或真实数据库。测试套件本身也要遵守整洁架构，否则会脆弱到&amp;quot;一改代码就挂一片&amp;quot;，反过来吓得团队不敢重构。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第六部分-实现细节震碎三观的四章&#34;&gt;第六部分 实现细节：震碎三观的四章&lt;/h2&gt;
&lt;p&gt;这一部分只反复强调一件事：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;数据库是细节。Web 是细节。框架是细节。它们都是可以被替换、被推迟决策的 IO 设备。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;数据库只是实现细节&#34;&gt;数据库只是实现细节&lt;/h3&gt;
&lt;p&gt;关系数据库本质上只是一种&amp;quot;把数据组织在磁盘上方便存取&amp;quot;的技术，它对业务逻辑而言就是一个细节。业务模型关注的是数据结构和操作，不是表结构和 SQL。今天用 MySQL，明天换 MongoDB，核心业务逻辑应该无感。&lt;/p&gt;
&lt;h3 id=&#34;web-是实现细节&#34;&gt;Web 是实现细节&lt;/h3&gt;
&lt;p&gt;Web 只是一种 IO 设备。过去几十年 GUI 技术换了一茬又一茬（桌面 → Web → 移动 → …），Web 也终将过时。架构要做的是把系统核心设计成与设备无关，让&amp;quot;通过 Web 交付&amp;quot;成为一种可插拔的交付方式。&lt;/p&gt;
&lt;h3 id=&#34;应用程序框架是实现细节&#34;&gt;应用程序框架是实现细节&lt;/h3&gt;
&lt;p&gt;框架作者对你的人生没有承诺——框架升级时的破坏式变更、框架的局限性绑架，都是真实风险。正确姿势：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;与框架保持&amp;quot;单向婚姻&amp;quot;：你可以依赖框架，但不让框架侵入核心业务代码。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;不要从框架基类派生业务对象，不要在核心业务层用框架的注解——把框架摁在最外层。&lt;/p&gt;
&lt;h3 id=&#34;案例分析视频销售网站&#34;&gt;案例分析：视频销售网站&lt;/h3&gt;
&lt;p&gt;书里用一个&amp;quot;在线卖视频的网站&amp;quot;贯穿分析了完整的架构过程：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;先识别&lt;strong&gt;用例和 actor&lt;/strong&gt;（购买者、管理员、作者等）；&lt;/li&gt;
&lt;li&gt;按 actor 和变更原因划分&lt;strong&gt;组件边界&lt;/strong&gt;（视图、用例、实体、数据库各自成组）；&lt;/li&gt;
&lt;li&gt;用&lt;strong&gt;依赖反转&lt;/strong&gt;保证所有依赖指向高层策略；&lt;/li&gt;
&lt;li&gt;最后得到一个可以按团队分工并行开发、按需单体或分组件部署的结构。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;结论：架构设计的输入不是技术选型，而是&lt;strong&gt;用例、行为者、变更原因&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id=&#34;拾遗simon-brown-执笔打包方式的争论&#34;&gt;拾遗（Simon Brown 执笔）：打包方式的争论&lt;/h3&gt;
&lt;p&gt;最后一章讨论了四种代码组织方式，相当接地气：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TD
    A[&amp;#34;① 按层打包&amp;lt;br/&amp;gt;controllers/ services/ repositories/&amp;lt;br/&amp;gt;（经典但业务意图不明显）&amp;#34;] 
    B[&amp;#34;② 按功能打包&amp;lt;br/&amp;gt;orders/ customers/ payments/&amp;lt;br/&amp;gt;（业务意图清晰）&amp;#34;]
    C[&amp;#34;③ 端口与适配器&amp;lt;br/&amp;gt;domain 与 infrastructure 分离&amp;lt;br/&amp;gt;（六边形架构风格）&amp;#34;]
    D[&amp;#34;④ 按组件打包&amp;lt;br/&amp;gt;粗粒度组件 + 严格的访问控制&amp;lt;br/&amp;gt;（Bob 大叔推荐，配合编译器/访问修饰符强制隔离）&amp;#34;]
    A -.演进.-&amp;gt; B -.演进.-&amp;gt; C -.演进.-&amp;gt; D&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;要点是：无论选哪种，&lt;strong&gt;关键是用编译器（访问修饰符、模块系统）来强制执行依赖规则&lt;/strong&gt;，而不是靠&amp;quot;大家自觉&amp;quot;。架构约定如果没有工具强制，迟早被腐蚀。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;实战举例一个订单服务的整洁化改造&#34;&gt;实战举例：一个订单服务的&amp;quot;整洁化&amp;quot;改造&lt;/h2&gt;
&lt;p&gt;光看理论容易晕，用一个简化的 Java/Spring 风格例子收尾。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;改造前&lt;/strong&gt;（典型的三层贫血架构，业务逻辑泄漏到各处）：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// Controller 直接调 Repository，业务规则散落在 SQL 和 Service 里&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@RestController&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;OrderController&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;@Autowired&lt;/span&gt; OrderRepository repo;      &lt;span style=&#34;color:#75715e&#34;&gt;// 直接依赖 JPA/SQL&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;@PostMapping&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/orders&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;create&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;@RequestBody&lt;/span&gt; OrderEntity e) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (e.&lt;span style=&#34;color:#a6e22e&#34;&gt;getAmount&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; 10000 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;e.&lt;span style=&#34;color:#a6e22e&#34;&gt;getVip&lt;/span&gt;()) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;throw&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; RuntimeException(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;超限&amp;#34;&lt;/span&gt;);   &lt;span style=&#34;color:#75715e&#34;&gt;// 业务规则混在 Web 层&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        repo.&lt;span style=&#34;color:#a6e22e&#34;&gt;save&lt;/span&gt;(e);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;问题：想换成命令行下单？重写。想换掉数据库？重写。想单测这条&amp;quot;超 1 万必须是 VIP&amp;quot;的规则？得把 Spring 容器和数据库拉起来。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;改造后&lt;/strong&gt;（按整洁架构分层）：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// ① 实体层：与企业无关环境的核心规则，零依赖&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;OrderPolicy&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;boolean&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;canPlace&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;long&lt;/span&gt; amount, &lt;span style=&#34;color:#66d9ef&#34;&gt;boolean&lt;/span&gt; vip) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; amount &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;=&lt;/span&gt; 10000 &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; vip;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// ② 用例层：定义输入/输出端口，依赖反转&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;interface&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;OrderGateway&lt;/span&gt; {                 &lt;span style=&#34;color:#75715e&#34;&gt;// 用例层定义的接口（输出端口）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;save&lt;/span&gt;(String orderId, &lt;span style=&#34;color:#66d9ef&#34;&gt;long&lt;/span&gt; amount, &lt;span style=&#34;color:#66d9ef&#34;&gt;boolean&lt;/span&gt; vip);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;PlaceOrderUseCase&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;final&lt;/span&gt; OrderGateway gateway;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    PlaceOrderUseCase(OrderGateway gateway) { &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;gateway&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; gateway; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    String &lt;span style=&#34;color:#a6e22e&#34;&gt;execute&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;long&lt;/span&gt; amount, &lt;span style=&#34;color:#66d9ef&#34;&gt;boolean&lt;/span&gt; vip) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;OrderPolicy.&lt;span style=&#34;color:#a6e22e&#34;&gt;canPlace&lt;/span&gt;(amount, vip))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;REJECTED&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        String id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; UUID.&lt;span style=&#34;color:#a6e22e&#34;&gt;randomUUID&lt;/span&gt;().&lt;span style=&#34;color:#a6e22e&#34;&gt;toString&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        gateway.&lt;span style=&#34;color:#a6e22e&#34;&gt;save&lt;/span&gt;(id, amount, vip);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; id;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// ③ 接口适配层 + 最外层：Controller / Repository 实现端口&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@RestController&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;OrderController&lt;/span&gt; {                    &lt;span style=&#34;color:#75715e&#34;&gt;// Controller 是适配器&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;final&lt;/span&gt; PlaceOrderUseCase useCase;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    OrderController(PlaceOrderUseCase u) { &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;useCase&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; u; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;@PostMapping&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/orders&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ResponseEntity&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;String&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;create&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;@RequestBody&lt;/span&gt; OrderRequest req) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; ResponseEntity.&lt;span style=&#34;color:#a6e22e&#34;&gt;ok&lt;/span&gt;(useCase.&lt;span style=&#34;color:#a6e22e&#34;&gt;execute&lt;/span&gt;(req.&lt;span style=&#34;color:#a6e22e&#34;&gt;amount&lt;/span&gt;(), req.&lt;span style=&#34;color:#a6e22e&#34;&gt;vip&lt;/span&gt;()));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@Repository&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;SqlOrderGateway&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;implements&lt;/span&gt; OrderGateway {  &lt;span style=&#34;color:#75715e&#34;&gt;// 数据库是插件&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 这里可以写 SQL/JPA，内层完全不知道&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;save&lt;/span&gt;(String id, &lt;span style=&#34;color:#66d9ef&#34;&gt;long&lt;/span&gt; amount, &lt;span style=&#34;color:#66d9ef&#34;&gt;boolean&lt;/span&gt; vip) { &lt;span style=&#34;color:#75715e&#34;&gt;/* INSERT ... */&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;改造带来的可验证收益：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;可测试&lt;/strong&gt;：&lt;code&gt;new PlaceOrderUseCase(inMemoryGateway)&lt;/code&gt; 直接跑单元测试，不需要 Spring、不需要数据库；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可替换 UI&lt;/strong&gt;：再加一个 &lt;code&gt;OrderCliRunner&lt;/code&gt; 调用同一个 &lt;code&gt;PlaceOrderUseCase&lt;/code&gt;，一行用例代码不改；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可替换数据库&lt;/strong&gt;：写一个 &lt;code&gt;MongoOrderGateway implements OrderGateway&lt;/code&gt;，在 Main（配置类）里换掉注入的实现即可。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这个例子里每一条收益，都直接来自&amp;quot;依赖关系规则&amp;quot;那一条铁律。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;全书要点一页纸&#34;&gt;全书要点一页纸&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;部分&lt;/th&gt;
					&lt;th&gt;核心问题&lt;/th&gt;
					&lt;th&gt;一句话答案&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;概述&lt;/td&gt;
					&lt;td&gt;架构是为了什么&lt;/td&gt;
					&lt;td&gt;最小化系统全生命周期的人力成本&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;编程范式&lt;/td&gt;
					&lt;td&gt;代码的底层约束&lt;/td&gt;
					&lt;td&gt;三大范式都是&amp;quot;剥夺能力&amp;quot;：限制 goto、函数指针、赋值&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;SOLID&lt;/td&gt;
					&lt;td&gt;类与函数怎么设计&lt;/td&gt;
					&lt;td&gt;中层五原则，其中 DIP 是架构边界的武器&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;组件原则&lt;/td&gt;
					&lt;td&gt;类怎么打包、组件怎么依赖&lt;/td&gt;
					&lt;td&gt;聚合（REP/CCP/CRP）与耦合（ADP/SDP/SAP）的权衡&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;软件架构&lt;/td&gt;
					&lt;td&gt;系统怎么分层&lt;/td&gt;
					&lt;td&gt;划边界 + 依赖规则：依赖永远指向高层策略&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;实现细节&lt;/td&gt;
					&lt;td&gt;数据库/Web/框架是什么&lt;/td&gt;
					&lt;td&gt;都是细节、都是插件、都可以推迟决策&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;如果只能记住一句话：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;好的架构让最重要的东西（业务规则）不依赖任何东西，让最不重要的东西（数据库、Web、框架）依赖业务规则——依赖方向永远由外向内。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;参考资料：《Clean Architecture: A Craftsman&amp;rsquo;s Guide to Software Structure and Design》，Robert C. Martin 著，&lt;a href=&#34;https://github.com/gdut-yy/Clean-Architecture-zh&#34;&gt;中文译本&lt;/a&gt;。&lt;/em&gt;&lt;/p&gt;
</description>
    </item>
    <item>
      <title>《领域驱动设计·精简版》详解：零基础读懂 DDD 的核心概念</title>
      <link>https://ftmao.com/blog/posts/domain-driven-design-quickly-book-notes/</link>
      <pubDate>Sat, 29 Jul 2023 14:00:00 +0800</pubDate>
      <guid>https://ftmao.com/blog/posts/domain-driven-design-quickly-book-notes/</guid>
      <description>&lt;h1 id=&#34;领域驱动设计精简版详解零基础读懂-ddd-的核心概念&#34;&gt;《领域驱动设计·精简版》详解：零基础读懂 DDD 的核心概念&lt;/h1&gt;
&lt;p&gt;DDD 要回答的问题是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;软件是为了解决现实业务问题而存在的，可为什么代码写着写着，就和业务彻底脱节了？&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Eric Evans 的答案是：问题出在&amp;quot;模型&amp;quot;上。业务在领域专家脑子里，代码在程序员手里，中间隔着层层&amp;quot;翻译&amp;quot;，每翻译一次就失真一次。DDD 的全部内容，就是围绕&amp;quot;如何建立一个好模型、并让模型直接活在代码里&amp;quot;展开的。&lt;/p&gt;
&lt;p&gt;本文面向&lt;strong&gt;从未接触过 DDD&lt;/strong&gt; 的读者，按全书结构逐章展开，配合图解和实例，帮你建立完整的知识地图。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第-1-章-简介软件为什么会偏离领域&#34;&gt;第 1 章 简介：软件为什么会偏离领域&lt;/h2&gt;
&lt;h3 id=&#34;领域与领域模型&#34;&gt;领域与领域模型&lt;/h3&gt;
&lt;p&gt;**领域（Domain）**就是软件要解决的那个现实问题。做银行系统，金融业务就是领域；做空中交通监控，航线和飞行就是领域。&lt;/p&gt;
&lt;p&gt;书里用了一个类比：造汽车。工人们各自负责某个零件，但如果没人理解整车，造出来的只是一堆零件的集合体，不是一辆好车。软件也一样——&lt;strong&gt;你不能在对金融业务一无所知的情况下写出好的银行软件&lt;/strong&gt;。谁懂业务？不是架构师，不是分析师，是&lt;strong&gt;领域专家&lt;/strong&gt;（银行的业务人员、空管的调度员）。&lt;/p&gt;
&lt;p&gt;但领域知识是海量而杂乱的，不可能全部塞进软件。我们需要从中建立&lt;strong&gt;抽象&lt;/strong&gt;——即&lt;strong&gt;领域模型（Domain Model）&lt;/strong&gt;：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;模型不是一张图，也不是专家脑子里的全部知识，而是&amp;quot;经过严格组织并进行选择性抽象的知识&amp;quot;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;建模意味着取舍。银行软件会记录客户的住址，但绝不会记录客户眼睛的颜色——前者是业务需要的，后者不是。&lt;strong&gt;选择保留什么、放弃什么，正是建模的核心工作&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id=&#34;书里的案例空中交通监控系统&#34;&gt;书里的案例：空中交通监控系统&lt;/h3&gt;
&lt;p&gt;全书反复使用同一个例子：空中交通监控系统。飞机（Aircraft）按计划航线（Route）飞行，航线由一系列定位点（Fix）组成，每个定位点是一个三维坐标（3DPoint：经度、纬度、海拔）。系统接收雷达数据，计算飞机的真实飞行轨道，发现两架飞机航线可能交叉时向管制员报警。&lt;/p&gt;
&lt;p&gt;最初的设计里，Route 直接关联 3DPoint，后来团队发现：地面屏幕上显示的航线其实只需要二维投影，于是模型被重构为 Route 关联 2DPoint——&lt;strong&gt;模型随理解的加深而演进&lt;/strong&gt;，这正是 DDD 强调的动态过程。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第-2-章-通用语言ubiquitous-language&#34;&gt;第 2 章 通用语言（Ubiquitous Language）&lt;/h2&gt;
&lt;h3 id=&#34;翻译是万恶之源&#34;&gt;翻译是万恶之源&lt;/h3&gt;
&lt;p&gt;开发人员和领域专家说着两种语言：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;开发人员满脑子是类、方法、继承、多态、框架；&lt;/li&gt;
&lt;li&gt;领域专家满口是飞机、航线、海拔、偏航告警。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;于是每次沟通都在&amp;quot;翻译&amp;quot;：开发人员把业务术语翻译成技术概念去写代码，再把技术实现翻译回业务语言去汇报。&lt;strong&gt;每一次翻译都丢失信息，错误理解就这样悄悄积累&lt;/strong&gt;。更糟的是，很多时候团队连&amp;quot;翻译错了&amp;quot;都发现不了，直到软件上线才爆发。&lt;/p&gt;
&lt;h3 id=&#34;用模型当语言的骨架&#34;&gt;用模型当语言的骨架&lt;/h3&gt;
&lt;p&gt;DDD 的核心原则之一：&lt;strong&gt;使用一种基于模型的语言进行所有交流&lt;/strong&gt;——讨论用它，文档用它，代码里的类名、方法名也用它。因为语言贯穿所有交流形式，所以叫&lt;strong&gt;通用语言（Ubiquitous Language）&lt;/strong&gt;。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    subgraph 传统方式[&amp;#34;传统方式：靠翻译，层层失真&amp;#34;]
        E1[&amp;#34;领域专家&amp;lt;br/&amp;gt;（业务行话）&amp;#34;] --&amp;gt;|需求翻译| A1[&amp;#34;分析师文档&amp;#34;]
        A1 --&amp;gt;|设计翻译| D1[&amp;#34;开发人员&amp;#34;]
        D1 --&amp;gt;|代码实现| C1[(&amp;#34;代码&amp;lt;br/&amp;gt;（第三套术语）&amp;#34;)]
    end
    subgraph DDD方式[&amp;#34;DDD 方式：一种语言贯通&amp;#34;]
        E2[&amp;#34;领域专家&amp;#34;] &amp;lt;==&amp;gt;|&amp;#34;通用语言&amp;lt;br/&amp;gt;（基于领域模型）&amp;#34;| D2[&amp;#34;开发人员&amp;#34;]
        D2 --- C2[(&amp;#34;代码 = 模型&amp;lt;br/&amp;gt;术语完全一致&amp;#34;)]
        E2 --- C2
    end&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;实例&lt;/strong&gt;：空管系统里有个概念叫&amp;quot;偏航告警&amp;quot;。如果代码里叫 &lt;code&gt;YawDeviationWarningProcessor&lt;/code&gt;，而管制员嘴里叫&amp;quot;偏航告警&amp;quot;，需求评审时大家讨论的是 A，代码里实现的是 B，三个月后没人能对上号。按 DDD 的做法，团队约定统一叫&amp;quot;偏航告警（RouteDeviationAlert）&amp;quot;，类名、数据库表、会议讨论全部用同一个词——模型和代码之间不再存在翻译层。&lt;/p&gt;
&lt;p&gt;通用语言不是一次定稿的，它随模型的演进而演进：&lt;strong&gt;当你发现某个词说不清一件事，往往不是表达问题，而是模型有问题&lt;/strong&gt;——这就是重构的信号。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第-3-章-模型驱动设计model-driven-design&#34;&gt;第 3 章 模型驱动设计（Model-Driven Design）&lt;/h2&gt;
&lt;p&gt;这是全书篇幅最大的一章，讲模型怎么落地成代码。Eric Evans 提供了一套标准化的&amp;quot;构造块&amp;quot;（Building Blocks）。&lt;/p&gt;
&lt;h3 id=&#34;分层架构&#34;&gt;分层架构&lt;/h3&gt;
&lt;p&gt;先把领域层隔离出来，别让业务逻辑散落在界面和数据库代码里：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    UI[&amp;#34;用户界面层&amp;lt;br/&amp;gt;展示信息、接收输入&amp;#34;]
    APP[&amp;#34;应用层&amp;lt;br/&amp;gt;协调任务、编排流程&amp;lt;br/&amp;gt;（很薄，不含业务规则）&amp;#34;]
    DOM[&amp;#34;领域层&amp;lt;br/&amp;gt;★ 业务概念、业务规则、业务状态&amp;lt;br/&amp;gt;模型的家&amp;#34;]
    INFRA[&amp;#34;基础设施层&amp;lt;br/&amp;gt;持久化、消息、框架支撑&amp;#34;]
    UI --&amp;gt; APP --&amp;gt; DOM
    INFRA -.支撑.- DOM&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;关键原则：&lt;strong&gt;领域层不依赖界面，不被基础设施污染&lt;/strong&gt;。判断标准很简单——把界面整个换掉（比如从网页换成 App），领域层应该一行都不用改。&lt;/p&gt;
&lt;h3 id=&#34;七个构造块&#34;&gt;七个构造块&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    subgraph 领域层[&amp;#34;领域层的构造块&amp;#34;]
        direction TB
        E[&amp;#34;实体 Entity&amp;lt;br/&amp;gt;有身份，有生命周期&amp;#34;]
        VO[&amp;#34;值对象 Value Object&amp;lt;br/&amp;gt;无身份，不可变&amp;#34;]
        S[&amp;#34;服务 Service&amp;lt;br/&amp;gt;不属于任何对象的操作&amp;#34;]
        M[&amp;#34;模块 Module&amp;lt;br/&amp;gt;按概念分组&amp;#34;]
        AGG[&amp;#34;聚合 Aggregate&amp;lt;br/&amp;gt;一致性边界 + 聚合根&amp;#34;]
        F[&amp;#34;工厂 Factory&amp;lt;br/&amp;gt;封装复杂创建&amp;#34;]
        R[&amp;#34;资源库 Repository&amp;lt;br/&amp;gt;封装查询与重建&amp;#34;]
        AGG --&amp;gt; E
        AGG --&amp;gt; VO
        F --&amp;gt; AGG
        R --&amp;gt; AGG
    end&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h4 id=&#34;-实体entity靠身份区分&#34;&gt;① 实体（Entity）：靠&amp;quot;身份&amp;quot;区分&lt;/h4&gt;
&lt;p&gt;有些对象，即使属性全变了，它还是&amp;quot;它&amp;quot;。一架飞机换了涂装、换了座椅布局，航班号 CA1234 还是那架飞机。&lt;strong&gt;实体由身份标识（ID）定义，而不是属性&lt;/strong&gt;。人也是典型的实体——同名同姓的两个人依然是两个人。&lt;/p&gt;
&lt;h4 id=&#34;-值对象value-object靠属性区分&#34;&gt;② 值对象（Value Object）：靠&amp;quot;属性&amp;quot;区分&lt;/h4&gt;
&lt;p&gt;有些对象没有身份，只描述&amp;quot;是什么&amp;quot;。地址&amp;quot;北京市海淀区某路 1 号&amp;quot;就是值对象：两个地址属性完全相同，就是同一个地址，可以互换、可以共享。值对象应该是&lt;strong&gt;不可变的&lt;/strong&gt;——要改就新建一个。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;新手最常见的错误是把一切都建成实体&lt;/strong&gt;。书里建议：优先考虑值对象，只有当&amp;quot;身份&amp;quot;确实重要时才用实体——值对象更简单、更安全、性能更好。&lt;/p&gt;
&lt;h4 id=&#34;-服务service无家可归的操作&#34;&gt;③ 服务（Service）：无家可归的操作&lt;/h4&gt;
&lt;p&gt;有些操作天然不属于任何实体或值对象。比如&amp;quot;转账&amp;quot;：钱从 A 账户到 B 账户，这个操作该挂在 A 身上还是 B 身上？都不合适。把它做成一个领域服务 &lt;code&gt;TransferService&lt;/code&gt;。判断标准：&lt;strong&gt;操作的概念属于领域，但它本身是无状态的&lt;/strong&gt;。&lt;/p&gt;
&lt;h4 id=&#34;-模块module高内聚低耦合的分组&#34;&gt;④ 模块（Module）：高内聚、低耦合的分组&lt;/h4&gt;
&lt;p&gt;把概念上紧密相关的类放进同一个模块。&lt;strong&gt;模块名也应该进入通用语言&lt;/strong&gt;——当团队说&amp;quot;这是计费模块的事&amp;quot;，所有人都知道边界在哪。&lt;/p&gt;
&lt;h4 id=&#34;-聚合aggregate一致性的边界&#34;&gt;⑤ 聚合（Aggregate）：一致性的边界&lt;/h4&gt;
&lt;p&gt;对象之间关系复杂时，改一个对象可能牵连一片。聚合的解法是：&lt;strong&gt;把关联紧密的对象圈进一个边界，选一个实体做&amp;quot;聚合根&amp;quot;（Aggregate Root），外部只能通过根访问内部对象&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;书里的例子：客户（Customer）是聚合根，地址（Address）、联系人是内部对象。外部想要地址？找客户要，拿到的是一个拷贝，不能直接改——所有对聚合内部的修改都经过根把关，不变量（invariant）就有了守护者。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    subgraph 聚合边界[&amp;#34;聚合边界&amp;#34;]
        ROOT[&amp;#34;客户 Customer&amp;lt;br/&amp;gt;（聚合根）&amp;#34;]
        ADDR[&amp;#34;地址 Address&amp;#34;]
        CONTACT[&amp;#34;联系人 Contact&amp;#34;]
        ROOT --&amp;gt; ADDR
        ROOT --&amp;gt; CONTACT
    end
    OUT[&amp;#34;外部对象&amp;#34;] --&amp;gt;|&amp;#34;只能持有根的引用&amp;#34;| ROOT
    OUT -.-&amp;gt;|&amp;#34;禁止直接访问&amp;#34;| ADDR&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h4 id=&#34;-工厂factory封装怎么造&#34;&gt;⑥ 工厂（Factory）：封装&amp;quot;怎么造&amp;quot;&lt;/h4&gt;
&lt;p&gt;创建一个复杂聚合涉及大量知识：内部结构、对象间关系、初始规则。如果让调用方自己 &lt;code&gt;new&lt;/code&gt; 再拼装，等于&amp;quot;给你塑胶、橡胶、金属、硅，自己组装一台打印机&amp;quot;——组装知识泄漏到了所有调用点。&lt;strong&gt;工厂把创建逻辑收进一处&lt;/strong&gt;，客户只要说&amp;quot;我要一个完整的客户聚合&amp;quot;。&lt;/p&gt;
&lt;h4 id=&#34;-资源库repository封装怎么找&#34;&gt;⑦ 资源库（Repository）：封装&amp;quot;怎么找&amp;quot;&lt;/h4&gt;
&lt;p&gt;对象创建后存进数据库，之后要用时再取出来。资源库给领域对象提供一个&amp;quot;像集合一样&amp;quot;的查询接口（&lt;code&gt;findById&lt;/code&gt;、&lt;code&gt;findByName&lt;/code&gt;），把 SQL 和 ORM 细节全部藏在背后。&lt;strong&gt;领域代码面对的好像是一个内存中的对象集合&lt;/strong&gt;，至于底下是 MySQL 还是 MongoDB，领域层不关心。&lt;/p&gt;
&lt;p&gt;三者分工一句话：&lt;strong&gt;工厂管出生，资源库管找回，聚合管活着时的一致性&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第-4-章-面向深层理解的重构&#34;&gt;第 4 章 面向深层理解的重构&lt;/h2&gt;
&lt;p&gt;模型不是一次画对的。随着对业务理解的加深，团队会不断发现&amp;quot;原来的模型表达不了新需求&amp;quot;。DDD 的态度是：&lt;strong&gt;持续重构，并且专门朝&amp;quot;深层理解&amp;quot;的方向重构&lt;/strong&gt;——不是消除代码重复那种技术重构，而是当领域专家随口说出一个新概念时，敏锐地意识到&amp;quot;这个词值得成为模型里的一等公民&amp;quot;。&lt;/p&gt;
&lt;p&gt;书里称之为&lt;strong&gt;凸现关键概念&lt;/strong&gt;：把隐晦的、散落在各处的业务规则，提炼成显式的类和方法。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;实例&lt;/strong&gt;：空管系统最初把&amp;quot;两机航线交叉判断&amp;quot;写在一段几百行的过程代码里。重构后，团队把它提炼成 &lt;code&gt;FlightPathConflict&lt;/code&gt; 对象和 &lt;code&gt;conflictsWith()&lt;/code&gt; 方法——业务规则从&amp;quot;代码的副作用&amp;quot;变成了&amp;quot;模型中可以直接讨论的概念&amp;quot;。测试可以单独写，管制员和开发可以用同一个词讨论它。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第-5-章-保持模型一致性大型项目的生存法则&#34;&gt;第 5 章 保持模型一致性：大型项目的生存法则&lt;/h2&gt;
&lt;p&gt;前面讲的都是&amp;quot;一个团队、一个模型&amp;quot;的情况。企业级项目有多个团队并行开发，理想中&amp;quot;全公司统一的大模型&amp;quot;实际上根本维持不住——每个团队都知道自己后院里有什么，但对其他地方一无所知，改一处就可能破坏别人的功能。&lt;/p&gt;
&lt;p&gt;Eric Evans 的结论很反直觉：&lt;strong&gt;不要试图维持一个注定四分五裂的大模型，而是有意识地把大模型拆成几个小模型，每个模型有清晰的边界&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id=&#34;界定的上下文bounded-context&#34;&gt;界定的上下文（Bounded Context）&lt;/h3&gt;
&lt;p&gt;每个模型只在一个明确的边界内有效，这个边界叫界定的上下文。同一个词在不同上下文里可以有完全不同的含义：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;实例&lt;/strong&gt;：电商系统里&amp;quot;商品&amp;quot;这个词——&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在&lt;strong&gt;销售上下文&lt;/strong&gt;里，它有价格、促销、库存；&lt;/li&gt;
&lt;li&gt;在&lt;strong&gt;物流上下文&lt;/strong&gt;里，它只有重量、体积、包装方式；&lt;/li&gt;
&lt;li&gt;在&lt;strong&gt;售后上下文&lt;/strong&gt;里，它只有保修期和维修记录。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;试图用一个统一的 &lt;code&gt;Product&lt;/code&gt; 类满足三个团队，结果一定是个臃肿的&amp;quot;上帝类&amp;quot;。承认三个上下文各有各的&amp;quot;商品&amp;quot;，反而各自清晰。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    subgraph 销售上下文[&amp;#34;销售上下文&amp;#34;]
        P1[&amp;#34;商品：价格/促销/库存&amp;#34;]
    end
    subgraph 物流上下文[&amp;#34;物流上下文&amp;#34;]
        P2[&amp;#34;商品：重量/体积/包装&amp;#34;]
    end
    subgraph 售后上下文[&amp;#34;售后上下文&amp;#34;]
        P3[&amp;#34;商品：保修期/维修记录&amp;#34;]
    end
    P1 &amp;lt;--&amp;gt;|&amp;#34;上下文映射&amp;lt;br/&amp;gt;（翻译）&amp;#34;| P2
    P2 &amp;lt;--&amp;gt;|&amp;#34;上下文映射&amp;#34;| P3&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h3 id=&#34;上下文之间的六种关系模式&#34;&gt;上下文之间的六种关系模式&lt;/h3&gt;
&lt;p&gt;上下文不能各自为战，它们之间需要集成。书里给出了一组模式，按&amp;quot;两个团队的合作意愿和权力关系&amp;quot;来选择：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    Q{&amp;#34;两个上下文如何共处？&amp;#34;}
    Q --&amp;gt;|&amp;#34;愿意深度共享代码&amp;#34;| SK[&amp;#34;共享内核 Shared Kernel&amp;lt;br/&amp;gt;划出一块双方都依赖的小模型&amp;lt;br/&amp;gt;共同维护，改动需协商&amp;#34;]
    Q --&amp;gt;|&amp;#34;上下游关系明确&amp;#34;| CS[&amp;#34;客户-供应商 Customer-Supplier&amp;lt;br/&amp;gt;上游按下游需求规划开发&amp;#34;]
    Q --&amp;gt;|&amp;#34;下游无力影响上游&amp;#34;| CF[&amp;#34;顺从者 Conformist&amp;lt;br/&amp;gt;直接采用上游模型，放弃翻译&amp;#34;]
    Q --&amp;gt;|&amp;#34;要对接脏乱差的遗留系统&amp;#34;| ACL[&amp;#34;防崩溃层 Anticorruption Layer&amp;lt;br/&amp;gt;建一层翻译，把污染挡在门外&amp;#34;]
    Q --&amp;gt;|&amp;#34;集成成本 &amp;gt; 收益&amp;#34;| SW[&amp;#34;独立方法 Separate Ways&amp;lt;br/&amp;gt;干脆不集成，各做各的&amp;#34;]
    Q --&amp;gt;|&amp;#34;一个系统被多方集成&amp;#34;| OHS[&amp;#34;开放主机服务 Open Host Service&amp;lt;br/&amp;gt;定义标准协议，免去一对一翻译&amp;#34;]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;几个重点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;上下文映射（Context Map）&lt;/strong&gt;：把所有上下文和它们之间的关系画在一张图上。这张图是大型项目里最重要的架构文档——它让每个团队都看清&amp;quot;我的边界在哪、我和谁有契约&amp;quot;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;共享内核&lt;/strong&gt;：两个团队共用一小部分模型（比如通用的客户标识）。共享意味着牵一发而动全身，所以内核要&lt;strong&gt;尽量小&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;防崩溃层（ACL）&lt;/strong&gt;：对接遗留系统时，遗留模型往往概念混乱。如果直接在自己的代码里用它的概念，好模型会被污染。ACL 是一层专门的翻译器：外面进来的是遗留概念，翻成本地模型再入内。&lt;strong&gt;遗留系统的&amp;quot;腐烂&amp;quot;被挡在层外&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;顺从者 vs 防崩溃层&lt;/strong&gt;：下游对上游没有话语权时，要么顺从（直接用上游模型），要么自建翻译层。前者省成本，后者保纯洁——取决于上游模型的质量。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;开放主机服务&lt;/strong&gt;：当你的系统要被十个团队集成时，给每个团队定制翻译层会把你拖垮。定义一套标准协议（比如一套公共 API），所有集成方遵循它。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;精炼distillation找到核心域&#34;&gt;精炼（Distillation）：找到核心域&lt;/h3&gt;
&lt;p&gt;大模型拆小之后还有一个问题：&lt;strong&gt;哪里最值得投入最优秀的人力？&lt;strong&gt;答案是&lt;/strong&gt;核心域（Core Domain）&lt;/strong&gt;——代表领域本质、构成业务竞争力的那一小块。&lt;/p&gt;
&lt;p&gt;书里的例子非常精彩：空中交通监控系统里，&amp;ldquo;航线&amp;quot;看起来无处不在，但它其实是个普通概念，很多领域都有航线。系统真正的&amp;quot;心脏&amp;quot;是&lt;strong&gt;基于雷达数据实时计算飞行轨道、发现航线交叉并告警&lt;/strong&gt;的那个模块——这才是这套系统区别于任何普通航线系统的本质，也是最该投入最强团队、精雕细琢的地方。其余部分（普通子域）可以外包、可以买现成组件、可以交给初级团队。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;战略层面的启示：&lt;strong&gt;不是所有代码都值得写好&lt;/strong&gt;。把最好的资源压在核心域上，其他地方&amp;quot;够用就行&amp;rdquo;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第-6-章-专访-eric-evans&#34;&gt;第 6 章 专访 Eric Evans&lt;/h2&gt;
&lt;p&gt;书末是 InfoQ 对 Eric Evans 的访谈，几个观点值得记住：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;DDD 不是新方法，而是对二十年来优秀团队实践的系统化总结；&lt;/li&gt;
&lt;li&gt;DDD 与敏捷并不冲突，恰恰相反——没有好模型的敏捷团队，重构会越来越吃力；&lt;/li&gt;
&lt;li&gt;DDD 的难点不在技术，在于团队是否愿意持续地和领域专家坐在一起打磨模型和语言。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;全书总结一张-ddd-知识地图&#34;&gt;全书总结：一张 DDD 知识地图&lt;/h2&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;mindmap
  root((领域驱动设计&amp;lt;br/&amp;gt;DDD))
    核心思想
      软件是领域的映射
      模型是选择性抽象
      模型直接活在代码里
    通用语言
      一种语言贯穿讨论与代码
      说不清 = 模型有问题
    模型驱动设计
      分层架构
        界面层
        应用层
        领域层
        基础设施层
      构造块
        实体 = 身份
        值对象 = 属性 + 不可变
        服务 = 无状态操作
        聚合 = 一致性边界
        工厂 = 管创建
        资源库 = 管存取
    深层理解
      持续重构
      凸现关键概念
    保持模型一致性
      界定的上下文
      上下文映射
        共享内核
        客户-供应商
        顺从者
        防崩溃层
        独立方法
        开放主机服务
      精炼出核心域&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;DDD 最打动人的地方在于，它把软件开发从&amp;quot;写代码&amp;quot;重新定义为&amp;quot;&lt;strong&gt;持续理解业务、并把理解精确表达出来&lt;/strong&gt;&amp;ldquo;的过程。代码只是表达模型的介质，模型才是资产。&lt;/p&gt;
&lt;p&gt;如果你是零基础读者，建议按这个顺序消化：先吃透&amp;quot;通用语言&amp;rdquo;（它不需要任何技术背景，却能立刻改善团队协作），再掌握七个构造块（写代码时随时对照），最后在有大型项目或微服务需求时再深入&amp;quot;界定上下文&amp;quot;那一套——你会发现近年火热的微服务拆分方法论，思想源头正是这本书的 Bounded Context。&lt;/p&gt;
</description>
    </item>
    <item>
      <title>《软件架构：最难的部分》详解：分布式架构的权衡分析实战指南</title>
      <link>https://ftmao.com/blog/bookstore/software-architecture-hard-parts-book-notes/</link>
      <pubDate>Wed, 26 Jul 2023 14:00:00 +0800</pubDate>
      <guid>https://ftmao.com/blog/bookstore/software-architecture-hard-parts-book-notes/</guid>
      <description>&lt;h1 id=&#34;软件架构最难的部分详解分布式架构的权衡分析实战指南&#34;&gt;《软件架构：最难的部分》详解：分布式架构的权衡分析实战指南&lt;/h1&gt;
&lt;p&gt;《软件架构：最难的部分》（Software Architecture: The Hard Parts: Modern Trade-Off Analyses for Distributed Architectures）是 O&amp;rsquo;Reilly 在 2021 年出版的一本架构书，四位作者都是业内响当当的人物：Neal Ford（《软件架构 fundamentals》作者之一）、Mark Richards（资深架构师、《软件架构 fundamentals》合著者）、Pramod Sadalage（数据库演进专家）和 Zhamak Dehghani（&amp;ldquo;数据网格&amp;quot;概念的提出者）。&lt;/p&gt;
&lt;p&gt;这本书回答了一个非常现实的问题：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;当我把一个单体系统拆成微服务时，每一步到底该怎么拆？怎么知道拆得对不对？&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;市面上的架构书大多告诉你&amp;quot;什么是好架构&amp;rdquo;，却很少教你&amp;quot;做决策的过程&amp;quot;。这本书补上了这块空白。它的核心立场是：&lt;strong&gt;架构里不存在&amp;quot;最佳实践&amp;quot;，只存在&amp;quot;权衡分析&amp;quot;（Trade-off Analysis）&lt;/strong&gt;。每一个决策都是在拿一些东西换另一些东西，架构师的工作就是把交换的条件摆清楚。&lt;/p&gt;
&lt;p&gt;本文面向&lt;strong&gt;从未接触过软件架构概念&lt;/strong&gt;的读者，按全书十五章的顺序逐层展开，配合图解和实例，帮你建立完整的知识地图。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第-1-章-当没有最佳实践时会发生什么&#34;&gt;第 1 章 当没有&amp;quot;最佳实践&amp;quot;时会发生什么&lt;/h2&gt;
&lt;h3 id=&#34;一切皆权衡&#34;&gt;一切皆权衡&lt;/h3&gt;
&lt;p&gt;作者开篇就泼了一盆冷水：如果有人问你&amp;quot;微服务和单体哪个好&amp;quot;，唯一正确的回答是——&lt;strong&gt;看情况&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;架构决策和买东西很像。假如有人不问预算、不问用途，直接告诉你&amp;quot;买这辆车是最好的&amp;quot;，你肯定会觉得他不靠谱。架构也一样：任何脱离上下文的&amp;quot;最佳实践&amp;quot;都是耍流氓。架构师真正的工作流程是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;找到决策涉及的各个维度（性能、成本、耦合度、团队结构……）；&lt;/li&gt;
&lt;li&gt;分析每个维度上的得与失；&lt;/li&gt;
&lt;li&gt;把分析结果呈现给利益相关者，让他们在知情的情况下做选择。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;所以这本书的副标题才是&amp;quot;现代分布式架构的权衡分析&amp;quot;——&lt;strong&gt;最难的部分不是技术本身，而是权衡&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id=&#34;贯穿全书的案例sysops-squad-工单系统&#34;&gt;贯穿全书的案例：Sysops Squad 工单系统&lt;/h3&gt;
&lt;p&gt;为了方便讲解，作者虚构了一个贯穿全书的案例。Sysops Squad 是一家 IT 运维支持公司，客户（通常是企业的 IT 部门）遇到设备故障时提交&lt;strong&gt;工单（Ticket）&lt;/strong&gt;，系统把工单分配给合适的运维专家（Sysop），专家上门或远程修复，完成后填写处理报告。&lt;/p&gt;
&lt;p&gt;他们现在用的是一个大单体应用，随着业务增长越来越痛苦：改一个小功能要全量发布、一个模块的 bug 拖垮整个系统、高峰期扩容只能整体扩容。于是团队决定把它拆成微服务——&lt;strong&gt;这本书讲的就是这一路上遇到的每一个&amp;quot;难啃的骨头&amp;quot;&lt;/strong&gt;。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    subgraph 拆分前[&amp;#34;拆分前：单体应用&amp;#34;]
        M[&amp;#34;Sysops Squad 单体系统&amp;lt;br/&amp;gt;工单 + 用户 + 派单 + 报告 + 计费&amp;lt;br/&amp;gt;共享一个大数据库&amp;#34;]
    end
    subgraph 拆分后[&amp;#34;拆分后：微服务&amp;#34;]
        A[&amp;#34;工单服务&amp;#34;]
        B[&amp;#34;用户服务&amp;#34;]
        C[&amp;#34;派单服务&amp;#34;]
        D[&amp;#34;报告服务&amp;#34;]
        E[&amp;#34;计费服务&amp;#34;]
        DB1[(&amp;#34;工单库&amp;#34;)]
        DB2[(&amp;#34;用户库&amp;#34;)]
        DB3[(&amp;#34;派单库&amp;#34;)]
        DB4[(&amp;#34;报告库&amp;#34;)]
        DB5[(&amp;#34;计费库&amp;#34;)]
        A --- DB1
        B --- DB2
        C --- DB3
        D --- DB4
        E --- DB5
    end
    拆分前 --&amp;gt;|本书要讲的全过程| 拆分后&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;看起来很美好对吧？但魔鬼全在细节里：组件怎么划？数据库怎么拆？服务粒度多细合适？公共代码怎么办？跨服务的事务和数据访问怎么办？——这些就是后面十四章要逐一解决的问题。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第-2-章-识别软件架构中的耦合&#34;&gt;第 2 章 识别软件架构中的耦合&lt;/h2&gt;
&lt;h3 id=&#34;架构量子architecture-quantum&#34;&gt;架构量子（Architecture Quantum）&lt;/h3&gt;
&lt;p&gt;这是全书最重要的概念之一，也是后续所有分析的基本单位。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;架构量子&lt;/strong&gt;：系统中可以&lt;strong&gt;独立部署&lt;/strong&gt;的最小单元，包含它正常运行所必需的一切（代码、数据库、配置等）。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;判断一个东西是不是一个架构量子，问三个问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;能独立部署吗？&lt;/strong&gt;（改完它不用动别的部分就能上线）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;功能内聚性高吗？&lt;/strong&gt;（它内部的代码是围绕同一个业务目标组织的）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;耦合度高吗？&lt;/strong&gt;（量子内部耦合高没关系，量子之间耦合低才是目标）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一个单体应用就是一个架构量子；一套拆得干净的微服务，每个服务各自是一个量子；而&amp;quot;服务拆了但数据库还共享&amp;quot;的系统，量子边界其实还卡在数据库上——这是新手最容易掉进去的坑。&lt;/p&gt;
&lt;h3 id=&#34;静态耦合与动态耦合&#34;&gt;静态耦合与动态耦合&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;静态耦合&lt;/strong&gt;：编译期/部署期就能看到的依赖。比如 A 服务调用了共享库里的类，或者两个服务连同一个数据库 schema。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;动态耦合&lt;/strong&gt;：运行时才发生的调用关系。比如订单服务运行时调用支付服务的接口。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;动态耦合由三个维度决定，这是全书反复使用的分析框架：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;维度&lt;/th&gt;
					&lt;th&gt;两个选项&lt;/th&gt;
					&lt;th&gt;权衡点&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;通信（Communication）&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;同步 / 异步&lt;/td&gt;
					&lt;td&gt;同步简单直观，异步解耦但复杂&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;一致性（Consistency）&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;原子 / 最终&lt;/td&gt;
					&lt;td&gt;原子一致安全，最终一致可用性高&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;协调（Coordination）&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;编排 / 编制&lt;/td&gt;
					&lt;td&gt;编排好管理，编制无中心瓶颈&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;这三个维度像调色盘的三原色，后面讲工作流（第 11 章）和 Saga（第 12 章）时会反复用到。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第-3-章-架构模块化&#34;&gt;第 3 章 架构模块化&lt;/h2&gt;
&lt;p&gt;为什么要模块化？作者给出了五个驱动力，每一个都对应一种&amp;quot;不模块化会疼&amp;quot;的场景：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;可维护性（Maintainability）&lt;/strong&gt;：大泥球代码改一处坏三处，模块化让改动范围可控。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可测试性（Testability）&lt;/strong&gt;：边界清晰的模块可以单独写测试，不用拉起整个系统。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可部署性（Deployability）&lt;/strong&gt;：小模块发布风险低、频率高；单体发布一次像发射火箭。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可扩展性（Scalability）&lt;/strong&gt;：旺季只有&amp;quot;下单&amp;quot;需要扩容，模块化后可以只扩这一个模块。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可用性与容错（Availability/Fault Tolerance）&lt;/strong&gt;：故障被限制在模块内部，不至于全站瘫痪。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;举个生活化的例子：老式组合音响里一个喇叭坏了，整台机器都得拆开修；而现在的家庭影院，功放、音箱、播放器各自独立，坏哪个换哪个——这就是模块化的价值。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第-4-章-架构分解&#34;&gt;第 4 章 架构分解&lt;/h2&gt;
&lt;p&gt;拆分之前，先要学会&amp;quot;测量&amp;quot;耦合，而不是凭感觉。&lt;/p&gt;
&lt;h3 id=&#34;传入耦合与传出耦合&#34;&gt;传入耦合与传出耦合&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;传入耦合（Afferent Coupling）&lt;/strong&gt;：有多少别的组件依赖我？——衡量&amp;quot;我有多重要、改动我的风险有多大&amp;quot;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;传出耦合（Efferent Coupling）&lt;/strong&gt;：我依赖多少别的组件？——衡量&amp;quot;我有多容易被别人拖累&amp;quot;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;主序列距离distance-from-the-main-sequence&#34;&gt;主序列距离（Distance from the Main Sequence）&lt;/h3&gt;
&lt;p&gt;这是 Robert C. Martin 提出的一组度量。想象一条坐标轴：横轴是&lt;strong&gt;抽象性&lt;/strong&gt;（组件里抽象类/接口占的比例），纵轴是&lt;strong&gt;不稳定性&lt;/strong&gt;（传出耦合占比）。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一个组件如果&lt;strong&gt;很具体但很多人依赖它&lt;/strong&gt;（比如工具类被全系统引用），它落在&amp;quot;痛苦区&amp;quot;：难改、一改全崩。&lt;/li&gt;
&lt;li&gt;一个组件如果&lt;strong&gt;很抽象但没人用&lt;/strong&gt;（比如过度设计的框架），它落在&amp;quot;无用区&amp;quot;。&lt;/li&gt;
&lt;li&gt;理想位置在两者之间的对角线（&amp;ldquo;主序列&amp;rdquo;）附近。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些度量不是让你拿计算器天天算，而是提供一个&lt;strong&gt;客观的拆分依据&lt;/strong&gt;：拆分候选的组件，先看它们的耦合度量，优先拆那些传入传出耦合低、边界清晰的——它们是&amp;quot;最容易摘下来的果子&amp;quot;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第-5-章-基于组件的分解模式&#34;&gt;第 5 章 基于组件的分解模式&lt;/h2&gt;
&lt;p&gt;这是全书最&amp;quot;实操&amp;quot;的一章：怎么把一坨单体代码拆成组件？作者给出了一个六步流水线，每一步都有对应的&amp;quot;适应度函数&amp;quot;（自动化检查规则）来保证不跑偏。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    S1[&amp;#34;① 识别并确定组件大小&amp;lt;br/&amp;gt;盘点代码库里有哪些组件、各占多大比例&amp;#34;]
    S2[&amp;#34;② 收集公共组件&amp;lt;br/&amp;gt;把多个组件里重复出现的代码收拢到一处&amp;#34;]
    S3[&amp;#34;③ 扁平化组件&amp;lt;br/&amp;gt;把代码从根命名空间下沉到有明确职责的叶子节点&amp;#34;]
    S4[&amp;#34;④ 确定组件依赖&amp;lt;br/&amp;gt;画出组件依赖图，找出纠缠在一起的循环依赖&amp;#34;]
    S5[&amp;#34;⑤ 创建组件域&amp;lt;br/&amp;gt;把职责相近的组件归入同一个领域分组&amp;#34;]
    S6[&amp;#34;⑥ 创建领域服务&amp;lt;br/&amp;gt;每个领域组对外暴露成一个粗粒度服务&amp;#34;]
    S1 --&amp;gt; S2 --&amp;gt; S3 --&amp;gt; S4 --&amp;gt; S5 --&amp;gt; S6&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;几个步骤的直觉解释：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;第①步&lt;/strong&gt;像搬家前盘点家当：先知道自己有什么。适应度函数可以自动检查&amp;quot;任何组件不得超过代码库总量的 X%&amp;quot;、&amp;ldquo;组件大小不得偏离平均值 N 个标准差&amp;rdquo;，防止拆出巨型组件。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;第②步&lt;/strong&gt;解决重复：如果&amp;quot;日志工具&amp;quot;&amp;ldquo;日期处理&amp;quot;在十个组件里各抄了一份，先把它们收拢，否则拆开后会十处同步改。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;第③步&lt;/strong&gt;针对一种常见病：大量代码堆在根命名空间（比如所有代码都在 &lt;code&gt;com.sysops&lt;/code&gt; 下面不分层），说明系统从来没有真正的结构，必须先给每段代码找到归属。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;第④步&lt;/strong&gt;是分水岭：依赖图里纠缠不清的组件（A 调 B、B 调 C、C 又调 A）说明领域边界划分有误，要回到业务层面重新划。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;第⑤⑥步&lt;/strong&gt;完成从&amp;quot;代码组件&amp;quot;到&amp;quot;业务服务&amp;quot;的跃迁：同一域内的组件将来部署成一个服务，域与域之间的边界就是服务边界。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;**适应度函数（Fitness Function）**是本书反复强调的治理手段：把架构约束写成自动化测试，放进 CI 流水线。比如&amp;quot;报告域的组件不允许依赖计费域的组件&amp;quot;可以直接写成一条 ArchUnit 测试，谁违反了构建就失败——架构约束从&amp;quot;文档里的愿望&amp;quot;变成&amp;quot;会报警的护栏&amp;rdquo;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第-6-章-拆分运营数据&#34;&gt;第 6 章 拆分运营数据&lt;/h2&gt;
&lt;p&gt;代码拆完，真正的硬骨头来了：&lt;strong&gt;数据库&lt;/strong&gt;。很多团队服务拆得很漂亮，底下却还用同一个数据库——等于楼分了户、水电还是一户一表，根本没有真正解耦。&lt;/p&gt;
&lt;h3 id=&#34;拆开数据库的推力与阻力&#34;&gt;拆开数据库的推力与阻力&lt;/h3&gt;
&lt;p&gt;作者在拆数据这件事上建立了一个&amp;quot;天平&amp;quot;模型：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;拆分驱动因素（Data Disintegrators，推着你拆）：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;变更控制：一个服务改表结构，不用和别的团队协调。&lt;/li&gt;
&lt;li&gt;连接管理：数据库连接数有限，服务多了必须分库。&lt;/li&gt;
&lt;li&gt;可扩展性：可以按服务单独扩容数据库。&lt;/li&gt;
&lt;li&gt;容错：一个库挂了只影响一个服务。&lt;/li&gt;
&lt;li&gt;架构量子：共享数据库会把量子边界撑破。&lt;/li&gt;
&lt;li&gt;数据库类型优化：不同服务适合不同数据库（工单用关系库，日志用时序库）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;整合因素（Data Integrators，拽着你不让拆）：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;数据关系：外键、JOIN 在跨库之后全部失效。&lt;/li&gt;
&lt;li&gt;数据库事务：跨库没有 ACID 事务，必须面对分布式事务的复杂性。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;五步数据库分解法&#34;&gt;五步数据库分解法&lt;/h3&gt;
&lt;p&gt;书里给出了一个务实的渐进式拆分流程，核心思想是&amp;quot;&lt;strong&gt;先逻辑拆，再物理拆&lt;/strong&gt;&amp;quot;：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    P1[&amp;#34;① 分析数据库&amp;lt;br/&amp;gt;划分数据域&amp;#34;]
    P2[&amp;#34;② 把表分配到&amp;lt;br/&amp;gt;各数据域&amp;#34;]
    P3[&amp;#34;③ 数据库连接&amp;lt;br/&amp;gt;按域分离&amp;#34;]
    P4[&amp;#34;④ Schema 迁移到&amp;lt;br/&amp;gt;独立数据库服务器&amp;#34;]
    P5[&amp;#34;⑤ 切换到完全独立的&amp;lt;br/&amp;gt;数据库&amp;#34;]
    P1 --&amp;gt; P2 --&amp;gt; P3 --&amp;gt; P4 --&amp;gt; P5&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;关键是第③步：表还没物理分开，就先让各服务只连自己的 schema、只访问自己的表。这一步不动数据，只动代码和连接，风险小、可回滚。等代码层面确认拆分正确了，再做物理迁移（第④⑤步）。这种&amp;quot;小步快跑、每步可退&amp;quot;的思路贯穿全书。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第-7-章-服务粒度&#34;&gt;第 7 章 服务粒度&lt;/h2&gt;
&lt;p&gt;拆服务时最纠结的问题：&lt;strong&gt;一个服务到底该多大？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;作者又祭出了&amp;quot;天平&amp;quot;分析法——这次叫粒度解耦因素（Granularity Disintegrators，推着你往细拆）与粒度整合因素（Granularity Integrators，拽着你往粗合）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;往细拆的理由（Disintegrators）：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;服务范围与功能&lt;/strong&gt;：一个服务只干一件事。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;代码波动性&lt;/strong&gt;：如果系统里&amp;quot;促销规则&amp;quot;每周改、&amp;ldquo;订单核心&amp;quot;半年不动，就该把促销拆出去，免得高频改动反复惊扰稳定部分。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可扩展性与吞吐量&lt;/strong&gt;：只有搜索服务需要扛流量，拆细了才能单独扩容。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;容错&lt;/strong&gt;：非关键功能（比如推荐）挂了不该拖垮下单。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;安全&lt;/strong&gt;：处理支付的服务需要更高的安全等级和审计，独立出来好管控。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可扩展性（Extensibility）&lt;/strong&gt;：新功能以新服务的形式生长，不改老代码。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;往粗合的理由（Integrators）：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;数据库事务&lt;/strong&gt;：两个操作必须在同一个 ACID 事务里？那它们最好待在同一个服务里。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;工作流与编制&lt;/strong&gt;：跨服务的编排/编制又慢又难调试，粒度太细会制造大量分布式工作流。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;共享代码&lt;/strong&gt;：两个服务共享大量代码，可能说明它们本来就该是一个。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据关系&lt;/strong&gt;：两个服务频繁访问彼此的数据，拆分带来的网络开销和一致性成本可能超过收益。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;实例&lt;/strong&gt;：Sysops Squad 拆出&amp;quot;工单服务&amp;quot;后，争论要不要把&amp;quot;工单指派&amp;quot;再拆成独立服务。分析后发现：指派逻辑改动频繁（每周调规则）、需要单独的弹性伸缩（早高峰派单量是平时的 5 倍）——两个 disintegrator 都很强；而它与工单共享的表只有一张、没有跨库事务需求——integrator 很弱。于是拆。反过来，&amp;ldquo;工单备注&amp;quot;功能没有任何独立的扩展和发布需求，就留在工单服务里。&lt;/p&gt;
&lt;p&gt;这就是权衡分析的样子：&lt;strong&gt;不是数哪边的理由多，而是掂量哪边的理由重&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第-8-章-复用模式&#34;&gt;第 8 章 复用模式&lt;/h2&gt;
&lt;p&gt;多个服务都需要同一段代码（比如审计日志、认证），怎么办？书里给了四种方案：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;模式&lt;/th&gt;
					&lt;th&gt;做法&lt;/th&gt;
					&lt;th&gt;优点&lt;/th&gt;
					&lt;th&gt;缺点&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;代码复制&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;每个服务拷一份&lt;/td&gt;
					&lt;td&gt;零耦合、零依赖&lt;/td&gt;
					&lt;td&gt;改 bug 要改 N 处&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;共享库&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;打成 jar/npm 包共用&lt;/td&gt;
					&lt;td&gt;版本可控、无运行时开销&lt;/td&gt;
					&lt;td&gt;版本地狱、异构技术栈难共享&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;共享服务&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;独立部署一个公共服务&lt;/td&gt;
					&lt;td&gt;改动一处全生效、跨语言&lt;/td&gt;
					&lt;td&gt;网络开销、单点风险、变更影响面大&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Sidecar / 服务网格&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;功能放进每个服务的伴生容器&lt;/td&gt;
					&lt;td&gt;跨语言、运维统一&lt;/td&gt;
					&lt;td&gt;需要容器编排基础设施&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;选择思路很直接：&lt;strong&gt;变化越频繁、越偏向业务的代码，越不要共享&lt;/strong&gt;（宁肯复制）；&lt;strong&gt;越稳定、越偏向技术基础设施的能力&lt;/strong&gt;（日志、监控、鉴权），越适合共享或 Sidecar。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;实例&lt;/strong&gt;：Sysops Squad 的&amp;quot;专家评分算法&amp;quot;每个服务都在用，但它每月都在调优——选了代码复制，各服务按需演进。而&amp;quot;日志采集&amp;quot;用了 Sidecar，统一由运维团队维护。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第-9-章-数据所有权与分布式事务&#34;&gt;第 9 章 数据所有权与分布式事务&lt;/h2&gt;
&lt;p&gt;拆库之后立刻撞上两个难题：一张表被两个服务都要写，归谁？一个业务操作要跨两个服务写数据，怎么保证一致性？&lt;/p&gt;
&lt;h3 id=&#34;数据所有权&#34;&gt;数据所有权&lt;/h3&gt;
&lt;p&gt;先看所有权的两种场景：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;单一所有权&lt;/strong&gt;：一张表只有一个服务写，其他服务最多只读——理想情况。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;共同所有权&lt;/strong&gt;：多个服务都要写同一张表——必须拆开归属。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;处理共同所有权有三种技术：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    subgraph 表拆分技术[&amp;#34;表拆分：按列拆开&amp;#34;]
        T1[&amp;#34;customer 表&amp;lt;br/&amp;gt;姓名/联系方式 → 档案服务&amp;lt;br/&amp;gt;信用额度/账单地址 → 计费服务&amp;#34;]
    end
    subgraph 数据域技术[&amp;#34;数据域：按行拆开&amp;#34;]
        T2[&amp;#34;ticket 表&amp;lt;br/&amp;gt;进行中的工单 → 工单服务&amp;lt;br/&amp;gt;已归档的历史工单 → 报告服务&amp;#34;]
    end
    subgraph 委派技术[&amp;#34;委派：一主一从&amp;#34;]
        T3[&amp;#34;invoice 表归计费服务所有&amp;lt;br/&amp;gt;工单服务需要时&amp;lt;br/&amp;gt;通过接口委托计费服务写&amp;#34;]
    end&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h3 id=&#34;分布式事务的三种模式&#34;&gt;分布式事务的三种模式&lt;/h3&gt;
&lt;p&gt;跨服务更新数据时，怎么保持一致？&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;后台同步模式&lt;/strong&gt;：先各自写，后台进程定时对账、修复不一致。最简单，但不一致窗口最长，适合对实时性无所谓的数据（比如统计报表）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;编排请求模式&lt;/strong&gt;：由一个编排者按顺序调用各服务，全部确认后才算完成。一致性好，但慢，且编排者成了耦合中心。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;基于事件的模式&lt;/strong&gt;：写本地库后立刻发事件，其他服务异步消费。性能和可用性最好，但要想清楚&amp;quot;事件丢了怎么办&amp;rdquo;。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;实例&lt;/strong&gt;：工单完成后要同时更新&amp;quot;工单状态&amp;quot;和&amp;quot;专家积分&amp;rdquo;。如果用编排请求，用户要等两个服务都响应；用事件模式，工单服务先发 &lt;code&gt;TicketCompleted&lt;/code&gt; 事件立即返回，积分服务异步加分——用户快了几百毫秒，代价是积分查询页面可能晚几秒才更新。Sysops Squad 选了后者：&lt;strong&gt;最终一致性换了响应速度&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第-10-章-分布式数据访问&#34;&gt;第 10 章 分布式数据访问&lt;/h2&gt;
&lt;p&gt;读操作比写操作更常见：服务 A 需要服务 B 拥有的数据，怎么拿？四种模式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;服务间通信&lt;/strong&gt;：直接调 B 的接口。最简单，但每次查询都是一次网络往返，B 挂了 A 也查不了。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;列模式复制&lt;/strong&gt;：把需要的那几列数据复制一份到 A 的数据库（通过消息保持同步）。查询快、无运行时依赖，但数据有延迟、要处理重复治理。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;复制缓存&lt;/strong&gt;：B 把数据推进共享缓存（如 Redis），A 从缓存读。适合读多写少的参考数据。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据域&lt;/strong&gt;：把双方都要的数据干脆划给一个专门的&amp;quot;数据域服务&amp;quot;管理。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;选择的关键问题是：&amp;quot;&lt;strong&gt;这份数据 A 需要多新鲜？B 的可用性 A 能承受吗？&lt;/strong&gt;&amp;ldquo;答案不同，模式就不同。比如专家的联系信息（几乎不变）适合复制缓存；工单的实时状态（必须最新）只能走服务间通信。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第-11-章-管理分布式工作流&#34;&gt;第 11 章 管理分布式工作流&lt;/h2&gt;
&lt;p&gt;一个业务流程跨多个服务时，谁来当&amp;quot;导演&amp;rdquo;？两种风格：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    subgraph 编排[&amp;#34;编排 Orchestration：有个总指挥&amp;#34;]
        O[&amp;#34;编排者&amp;lt;br/&amp;gt;（工单流程服务）&amp;#34;]
        O --&amp;gt; A[&amp;#34;工单服务&amp;#34;]
        O --&amp;gt; B[&amp;#34;派单服务&amp;#34;]
        O --&amp;gt; C[&amp;#34;计费服务&amp;#34;]
        O --&amp;gt; D[&amp;#34;通知服务&amp;#34;]
    end&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    subgraph 编制[&amp;#34;编制 Choreography：舞者跟着音乐各自起舞&amp;#34;]
        A2[&amp;#34;工单服务&amp;#34;] --&amp;gt;|发事件| B2[&amp;#34;派单服务&amp;#34;]
        B2 --&amp;gt;|发事件| C2[&amp;#34;计费服务&amp;#34;]
        C2 --&amp;gt;|发事件| D2[&amp;#34;通知服务&amp;#34;]
    end&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;编排（Orchestration）&lt;/strong&gt;：设一个中心编排者，像乐队指挥，告诉每个服务&amp;quot;现在该你了&amp;quot;。流程一目了然、好监控、好报错；但编排者容易变成新的&amp;quot;小单体&amp;quot;，且是所有服务的耦合点。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;编制（Choreography）&lt;/strong&gt;：没有中心，每个服务监听事件、做完自己的事再发下一个事件，像舞者跟着音乐即兴配合。极度解耦、无单点；但流程分散在各处，&amp;ldquo;现在进行到哪一步了&amp;quot;这种简单问题都很难回答。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;还有一个关键细节：**工作流状态放哪？**编排模式下状态天然在编排者手里；编制模式下要么设一个专门的&amp;quot;状态所有者&amp;quot;服务，要么忍受状态分散的痛苦。&lt;/p&gt;
&lt;p&gt;经验法则：&lt;strong&gt;流程复杂、分支多、需要人工介入补偿的，用编排；流程线性、追求吞吐量的，用编制&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第-12-章-事务性-saga&#34;&gt;第 12 章 事务性 Saga&lt;/h2&gt;
&lt;p&gt;Saga 是分布式世界里替代 ACID 事务的方案：把一个跨服务的大事务拆成一串本地小事务，任何一步失败就按相反顺序执行&lt;strong&gt;补偿操作&lt;/strong&gt;（退款、释放库存……）回滚已完成的步骤。&lt;/p&gt;
&lt;p&gt;书里把第 2 章的三个维度组合起来，给 Saga 模式起了八个别致的名字（括号里的三个字母依次代表：一致性 a=原子/e=最终、通信 s=同步/a=异步、协调 o=编排/c=编制）：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;模式&lt;/th&gt;
					&lt;th&gt;组合&lt;/th&gt;
					&lt;th&gt;适用场景&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;史诗 Saga&lt;/strong&gt;（a,s,o）&lt;/td&gt;
					&lt;td&gt;原子+同步+编排&lt;/td&gt;
					&lt;td&gt;最接近传统事务，但分布式环境下最难实现&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;电话接龙 Saga&lt;/strong&gt;（a,s,c）&lt;/td&gt;
					&lt;td&gt;原子+同步+编制&lt;/td&gt;
					&lt;td&gt;既要原子性又不想有中心——又慢又脆，慎用&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;童话 Saga&lt;/strong&gt;（e,s,o）&lt;/td&gt;
					&lt;td&gt;最终+同步+编排&lt;/td&gt;
					&lt;td&gt;简单好用的常见选择&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;时间旅行 Saga&lt;/strong&gt;（e,s,c）&lt;/td&gt;
					&lt;td&gt;最终+同步+编制&lt;/td&gt;
					&lt;td&gt;高并发下的妥协方案&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;奇幻小说 Saga&lt;/strong&gt;（a,a,o）&lt;/td&gt;
					&lt;td&gt;原子+异步+编排&lt;/td&gt;
					&lt;td&gt;用异步提高吞吐，靠编排守住一致性&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;恐怖故事 Saga&lt;/strong&gt;（a,a,c）&lt;/td&gt;
					&lt;td&gt;原子+异步+编制&lt;/td&gt;
					&lt;td&gt;作者直言：别这么干&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;平行 Saga&lt;/strong&gt;（e,a,o）&lt;/td&gt;
					&lt;td&gt;最终+异步+编排&lt;/td&gt;
					&lt;td&gt;最实用的组合之一&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;文集 Saga&lt;/strong&gt;（e,a,c）&lt;/td&gt;
					&lt;td&gt;最终+异步+编制&lt;/td&gt;
					&lt;td&gt;最解耦、最高可用，也最难调试&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;起这些&amp;quot;俗气&amp;quot;的名字是故意的：&lt;strong&gt;好记&lt;/strong&gt;。团队讨论时说&amp;quot;这个流程用文集模式&amp;rdquo;，比说&amp;quot;最终一致+异步通信+编制协调的 Saga&amp;quot;高效得多。&lt;/p&gt;
&lt;p&gt;核心结论：&lt;strong&gt;越往左（原子一致），越安全越慢越脆；越往右（最终一致+异步+编制），越快越抗造，但你要为&amp;quot;中间状态&amp;quot;和&amp;quot;补偿逻辑&amp;quot;付出工程成本&lt;/strong&gt;。没有免费的午餐，只有明摆着的账单。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第-13-章-契约&#34;&gt;第 13 章 契约&lt;/h2&gt;
&lt;p&gt;服务之间的接口约定（Contract）该多严格？&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;严格契约&lt;/strong&gt;：字段类型、格式全部写死并强校验（如带 schema 的 RPC）。好处是错误在编译/调用瞬间就暴露；代价是任何字段变更都要双方同时升级，耦合很紧。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;宽松契约&lt;/strong&gt;：只约定必需字段，其余自由扩展（如 JSON + 约定）。灵活、可独立演进；代价是错误可能潜伏到运行时才爆发。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;还有两个进阶概念：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;消费者驱动的契约（Consumer-Driven Contracts）&lt;/strong&gt;：由服务的&lt;strong&gt;调用方&lt;/strong&gt;写下&amp;quot;我需要这些字段、这种格式&amp;quot;，作为测试放进提供方的流水线。提供方一改接口，立刻知道会弄坏谁——契约从&amp;quot;提供方说了算&amp;quot;变成&amp;quot;对消费者负责&amp;quot;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;戳记耦合（Stamp Coupling）&lt;/strong&gt;：调用时把一大坨数据结构整个传过去（比如把 50 个字段的 &lt;code&gt;Customer&lt;/code&gt; 对象传给只需要 &lt;code&gt;email&lt;/code&gt; 的服务）。对方被迫依赖整个结构的形状，改任何一个字段都可能波及无辜。&lt;strong&gt;只传对方真正需要的字段&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第-14-章-管理分析型数据&#34;&gt;第 14 章 管理分析型数据&lt;/h2&gt;
&lt;p&gt;运营数据（支撑业务运转）拆给了微服务，那&lt;strong&gt;分析数据&lt;/strong&gt;（做报表、跑 BI、训练模型）怎么办？作者梳理了三代方案：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    DW[&amp;#34;数据仓库&amp;lt;br/&amp;gt;ETL 抽取 → 清洗 → 集中存储&amp;lt;br/&amp;gt;结构化、为报表优化&amp;#34;] --&amp;gt; DL[&amp;#34;数据湖&amp;lt;br/&amp;gt;原始数据先倒进去&amp;lt;br/&amp;gt;用的时候再处理&amp;lt;br/&amp;gt;灵活但容易变成数据沼泽&amp;#34;] --&amp;gt; DM[&amp;#34;数据网格&amp;lt;br/&amp;gt;按领域分布式的数据产品&amp;lt;br/&amp;gt;数据像微服务一样被治理&amp;#34;]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;**数据网格（Data Mesh）**是本章的高潮，也是作者之一 Zhamak Dehghani 原创的概念。它的核心思想是把微服务的哲学搬到数据世界：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;领域拥有数据&lt;/strong&gt;：工单团队对自己的工单数据负责，而不是甩给中央数据团队。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据即产品&lt;/strong&gt;：数据要有明确的接口、质量承诺、文档，像 API 一样被消费。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;自助式数据平台&lt;/strong&gt;：基础设施团队提供平台，领域团队自助发布数据产品。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;联邦式治理&lt;/strong&gt;：全局规则（隐私、安全）统一，领域内部自治。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;作者也诚实地给出了&amp;quot;什么时候&lt;strong&gt;不要&lt;/strong&gt;用数据网格&amp;quot;：团队小、数据量不大、分析需求简单时，一个数据仓库就够了——&lt;strong&gt;架构量子越少越好，够用就行&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第-15-章-构建你自己的权衡分析&#34;&gt;第 15 章 构建你自己的权衡分析&lt;/h2&gt;
&lt;p&gt;最后一章是方法论总结：这套&amp;quot;天平分析&amp;quot;手艺怎么用到自己的项目里？&lt;/p&gt;
&lt;p&gt;几条核心建议：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;找出纠缠的维度&lt;/strong&gt;：性能和安全往往互相拉扯，先把一个决策涉及的所有维度列出来。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;用 MECE 原则列清单&lt;/strong&gt;：相互独立、完全穷尽，避免漏掉某个维度的代价。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;警惕&amp;quot;脱离上下文&amp;quot;的陷阱&lt;/strong&gt;：网上说&amp;quot;Netflix 用了 X 架构所以 X 好&amp;quot;——Netflix 的上下文（规模、团队、预算）和你完全不同，结论不能直接搬。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;定性分析优先于定量&lt;/strong&gt;：很多时候没法拿到精确数字，用&amp;quot;高/中/低&amp;quot;的比较级就够做决策了，别为了伪精确而瘫痪。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;只给结论，别淹没听众&lt;/strong&gt;：给业务方汇报时，先说&amp;quot;推荐方案 B，因为……&amp;quot;，细节放附录。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;远离蛇油和布道&lt;/strong&gt;：任何声称&amp;quot;无条件最优&amp;quot;的方案（银弹、框架布道者），默认先怀疑。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;全书总结一张权衡分析的思维导图&#34;&gt;全书总结：一张权衡分析的思维导图&lt;/h2&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;mindmap
  root((软件架构&amp;lt;br/&amp;gt;最难的部分))
    核心理念
      没有最佳实践
      一切皆权衡
      架构量子是分析单位
    怎么拆
      组件分解六步法
      数据库五步分解
      粒度天平
        Disintegrators 往细拆
        Integrators 往粗合
    拆完怎么办
      复用四模式
      数据所有权
      分布式数据访问
    跨服务协作
      编排 vs 编制
      八种 Saga
      严格 vs 宽松契约
    数据与分析
      数据仓库/湖
      数据网格
    方法论
      适应度函数
      MECE 分析
      警惕脱离上下文&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;这本书最有价值的不是任何一条具体结论，而是它示范的&lt;strong&gt;思考方式&lt;/strong&gt;：面对任何架构决策，先问&amp;quot;这个选择让我得到什么、失去什么&amp;quot;，把天平两端的砝码摆出来，再结合自己的上下文做判断。&lt;/p&gt;
&lt;p&gt;如果你是架构新手，建议阅读顺序是：先读第 1、2 章建立&amp;quot;权衡&amp;quot;和&amp;quot;架构量子&amp;quot;的心智模型，再读第 5、6、7 章学会&amp;quot;拆&amp;quot;，最后按需查阅第 8–14 章解决&amp;quot;拆完之后&amp;quot;的具体问题。第 15 章适合放在手边，每次做重大技术选型前翻一遍——它会让你少交很多学费。&lt;/p&gt;
</description>
    </item>
    
  </channel>
</rss>
