<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>SOLID on 方糖猫Blog</title>
    <link>https://ftmao.com/blog/tags/solid/</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/solid/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>
    
  </channel>
</rss>
