<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>服务拆分 on Liangweidong's blog</title><link>https://liangweidonggood.github.io/tags/%E6%9C%8D%E5%8A%A1%E6%8B%86%E5%88%86/</link><description>Recent content in 服务拆分 on Liangweidong's blog</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Tue, 09 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://liangweidonggood.github.io/tags/%E6%9C%8D%E5%8A%A1%E6%8B%86%E5%88%86/index.xml" rel="self" type="application/rss+xml"/><item><title>服务拆分:康威定律、DDD 领域建模与生产落地指南</title><link>https://liangweidonggood.github.io/p/fu-wu-chai-fen/</link><pubDate>Tue, 09 Jun 2026 00:00:00 +0000</pubDate><guid>https://liangweidonggood.github.io/p/fu-wu-chai-fen/</guid><description>&lt;img src="https://liangweidonggood.github.io/p/fu-wu-chai-fen/image/cover.jpg" alt="Featured image of post 服务拆分:康威定律、DDD 领域建模与生产落地指南" /&gt;&lt;h1 id="服务拆分康威定律ddd-领域建模与生产落地指南"&gt;服务拆分:康威定律、DDD 领域建模与生产落地指南
&lt;/h1&gt;
 &lt;blockquote&gt;
 &lt;p&gt;Java Web 微服务系列 · 第 5 篇 · 服务拆分
阅读时长:约 70 分钟
本文写于 2026 年 6 月
配套版本:Spring Cloud 2022.0.x / Spring Boot 3.x / Dubbo 3.2.x
前置阅读:&lt;a class="link" href="https://liangweidonggood.github.io/p/ji-zhu-xuan-xing/" &gt;《技术选型:为什么最终选了 Spring Cloud Alibaba + Dubbo 3》&lt;/a&gt;(系列第 4 篇)
后续衔接:&lt;a class="link" href="https://liangweidonggood.github.io/p/nacos-service-and-config-center/" &gt;《Nacos:Java 微服务的服务中心与配置中心怎么选、怎么用》&lt;/a&gt;(系列第 6 篇)&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h2 id="引子38-个微服务大泥球的那一年"&gt;引子:38 个微服务大泥球的那一年
&lt;/h2&gt;&lt;p&gt;2021 年初我接手一个团队的微服务架构,上一任架构师留下的&amp;quot;遗产&amp;quot;是这样的:8 个业务域、&lt;strong&gt;38 个 Spring Boot 服务、2 个 PHP 老系统、3 个 Python 脚本、1 个没人敢动的 jQuery 前端&lt;/strong&gt;。所有服务都通过 Eureka 注册,看起来&amp;quot;很微服务&amp;quot;,但剖开看内核是一场灾难:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;38 个服务&lt;strong&gt;共享同一个 MySQL 集群&lt;/strong&gt;(16 个分库),改一个字段要同步 12 张表&lt;/li&gt;
&lt;li&gt;几乎所有&amp;quot;业务服务&amp;quot;都依赖一个 9000 行的 &lt;code&gt;user-core&lt;/code&gt; 服务,这个服务被 27 个上游调用,改一个方法签名要发 27 个版本&lt;/li&gt;
&lt;li&gt;&lt;code&gt;order-service&lt;/code&gt;、&lt;code&gt;pay-service&lt;/code&gt;、&lt;code&gt;inventory-service&lt;/code&gt; 三个服务&lt;strong&gt;循环依赖&lt;/strong&gt;:&lt;code&gt;order&lt;/code&gt; 调 &lt;code&gt;pay&lt;/code&gt; 算价格、&lt;code&gt;pay&lt;/code&gt; 调 &lt;code&gt;order&lt;/code&gt; 查订单状态、&lt;code&gt;inventory&lt;/code&gt; 调 &lt;code&gt;pay&lt;/code&gt; 锁库存、&lt;code&gt;pay&lt;/code&gt; 又反过来调 &lt;code&gt;inventory&lt;/code&gt; 查库存数&lt;/li&gt;
&lt;li&gt;4 个团队抢一个共享代码仓库 &lt;code&gt;common-utils&lt;/code&gt;,每个团队都在里面塞自己的工具类,&lt;strong&gt;1 万 2 千行代码,谁都不敢删&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;一次普通的需求评审,需要协调&lt;strong&gt;5 个团队、2 周排期&lt;/strong&gt;才能上线,因为每个服务都有自己的&amp;quot;上线窗口&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;更让我崩溃的是 CEO 在季度会上问的问题:&amp;quot;&lt;strong&gt;我们已经是微服务了,为什么改个东西比 3 年前的单体还慢?&lt;/strong&gt;&amp;quot;&lt;/p&gt;
&lt;p&gt;我花了 3 个月调研、6 个月重构,才让这个系统从&amp;quot;38 个微服务大泥球&amp;quot;变成&amp;quot;12 个真正自治的服务&amp;quot;。这篇文章把那段经历的&lt;strong&gt;方法论 + 工具箱 + 实战案例 + 踩坑教训&lt;/strong&gt;完整写出来。&lt;/p&gt;
&lt;p&gt;本文不是讲 Spring Cloud 怎么配置(那是第 4 篇技术选型),也不是讲 Nacos 怎么部署(那是第 6 篇)。&lt;strong&gt;本文讲的是&amp;quot;为什么拆、怎么拆、拆错了怎么救&amp;quot;&lt;/strong&gt;——也就是&amp;quot;先有边界,再有服务&amp;quot;的整套思维框架。&lt;/p&gt;
&lt;p&gt;核心三块:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;康威定律 + Team Topologies&lt;/strong&gt;:为什么&amp;quot;组织&amp;quot;决定了&amp;quot;架构&amp;quot;,以及怎么&amp;quot;反向&amp;quot;利用它&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DDD 领域驱动设计&lt;/strong&gt;:怎么用战术工具(实体/值对象/聚合根/限界上下文)识别真正的业务边界&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;5 条工业级拆分原则 + 6 个生产案例 + 10 个踩坑清单&lt;/strong&gt;:把方法论落到代码、数据库、团队三个层面&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="一拆分之前先想清楚业务边界--团队边界--康威定律"&gt;一、拆分之前先想清楚:业务边界 = 团队边界 = 康威定律
&lt;/h2&gt;
 &lt;blockquote&gt;
 &lt;p&gt;&amp;ldquo;Any organization that designs a system&amp;hellip; will inevitably produce a design whose structure is a copy of the organization&amp;rsquo;s communication structure.&amp;rdquo;
—— Melvin Conway, 1967&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h3 id="11-康威定律的原始表述"&gt;1.1 康威定律的原始表述
&lt;/h3&gt;&lt;p&gt;1967 年,计算机科学家 Melvin Conway 在《How Do Committees Invent?》一文中提出一个观察:&lt;strong&gt;系统的设计结构,必然反映设计该系统的组织沟通结构&lt;/strong&gt;。也就是说,如果你的团队有 4 个后端、2 个前端、1 个 DBA,那产出的系统大概率是&amp;quot;前端-后端-DBA 三层架构&amp;quot;,而不是按业务域划分的微服务。&lt;/p&gt;
&lt;p&gt;康威定律不是技术规律,&lt;strong&gt;是社会学规律&lt;/strong&gt;——它描述的是&amp;quot;人和人怎么沟通&amp;quot;决定了&amp;quot;代码怎么组织&amp;quot;。一个团队 8 个人都在同一个 Slack 频道,他们写出来的代码大概率会在同一个代码仓库、用同一种技术栈、按同一种部署节奏发布。&lt;/p&gt;
&lt;h3 id="12-推论4-种系统设计映射组织结构"&gt;1.2 推论:4 种系统设计映射组织结构
&lt;/h3&gt;&lt;p&gt;康威定律有 4 个广为流传的推论(Jim McCarthy):&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Communication dictates design&lt;/strong&gt;:组织沟通结构决定系统设计&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Time and space&lt;/strong&gt;:时间(同步/异步) + 空间(同地/异地) 影响协作效率&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;There&amp;rsquo;s never enough time to do it right, but always enough time to do it over&lt;/strong&gt;:没时间做对,但有时间重做&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The design that succeeds is the one that&amp;rsquo;s actually built&lt;/strong&gt;:最终交付的设计才是设计&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;第 1 条是最致命的:&lt;strong&gt;你写的不是代码,是你团队沟通结构的镜像&lt;/strong&gt;。一个 50 人团队按&amp;quot;前端组/后端组/DBA 组/测试组/运维组&amp;quot;分,不管他们多努力按业务域拆微服务,最后产出的还是&amp;quot;前端-后端&amp;quot;双层架构——因为沟通壁垒在团队结构里。&lt;/p&gt;
&lt;h3 id="13-inverse-conway-maneuver先调组织再调架构"&gt;1.3 Inverse Conway Maneuver:先调组织,再调架构
&lt;/h3&gt;&lt;p&gt;既然康威定律说&amp;quot;组织决定架构&amp;quot;,那反过来:&lt;strong&gt;先调整组织结构,再让架构跟着变&lt;/strong&gt;。这就是 Jonny LeRoy 在 2009 年提出的 &lt;strong&gt;Inverse Conway Maneuver&lt;/strong&gt;(反向康威)。&lt;/p&gt;
&lt;p&gt;实际操作就 3 步:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;画组织结构图&lt;/strong&gt;:把当前团队的人员、汇报关系、Slack 频道、代码仓库权限全画出来&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;ul&gt;
&lt;li&gt;团队结构和系统结构一致 → 康威定律在生效,这是好事&lt;/li&gt;
&lt;li&gt;团队结构和系统结构不一致 → &lt;strong&gt;架构师在跟康威定律对着干&lt;/strong&gt;,这往往就是性能瓶颈的根因&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;我那次接手 38 个微服务大泥球时,画的组织结构图是这样的:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;团队 A(订单业务):6 人,负责 order / pay / inventory 三个服务&lt;/li&gt;
&lt;li&gt;团队 B(用户业务):4 人,负责 user / auth / address 三个服务&lt;/li&gt;
&lt;li&gt;团队 C(运营业务):5 人,负责 marketing / coupon / points 三个服务&lt;/li&gt;
&lt;li&gt;团队 D(基础平台):7 人,负责 gateway / config / monitor 等基础设施&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;但实际系统调用关系是:订单业务的服务有 30% 的调用跑到用户业务,15% 跑到基础平台,5% 跑到运营业务。&lt;strong&gt;团队边界和系统边界完全错位&lt;/strong&gt;——这就是为什么一次改动要协调 5 个团队。&lt;/p&gt;
&lt;p&gt;解决思路不是&amp;quot;让架构师硬性约束跨团队调用&amp;quot;(那只是治标),而是&lt;strong&gt;让团队边界和系统边界对齐&lt;/strong&gt;。具体怎么对齐?这就是 1.4 要讲的 Team Topologies。&lt;/p&gt;
&lt;h3 id="14-team-topologies4-种团队类型--3-种交互模式"&gt;1.4 Team Topologies:4 种团队类型 + 3 种交互模式
&lt;/h3&gt;&lt;p&gt;2019 年,Matthew Skelton 和 Manuel Pais 出版了《Team Topologies》,把康威定律落地成可操作的团队设计模式。&lt;strong&gt;核心是把团队分成 4 种类型,互相之间用 3 种交互模式协作&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4 种团队类型&lt;/strong&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 style="text-align: right"&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;Stream-aligned team&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;端到端负责一条业务流(从用户到数据库)&lt;/td&gt;
					&lt;td style="text-align: right"&gt;5-9&lt;/td&gt;
					&lt;td&gt;订单业务团队、用户中心团队&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Enabling team&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;帮助 Stream-aligned 团队提升能力&lt;/td&gt;
					&lt;td style="text-align: right"&gt;3-5&lt;/td&gt;
					&lt;td&gt;安全赋能团队、可观测性赋能团队&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Complicated-subsystem team&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;负责需要深度专业知识的复杂子系统&lt;/td&gt;
					&lt;td style="text-align: right"&gt;3-7&lt;/td&gt;
					&lt;td&gt;风控算法团队、推荐引擎团队&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Platform team&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;提供内部平台(自助服务)给 Stream-aligned 团队用&lt;/td&gt;
					&lt;td style="text-align: right"&gt;5-12&lt;/td&gt;
					&lt;td&gt;K8s 平台团队、CI/CD 平台团队&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;3 种交互模式&lt;/strong&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;Collaboration&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;X-as-a-Service&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;Facilitating&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;一个团队帮另一个团队&amp;quot;扫除障碍&amp;quot;&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;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;80% 的团队应该是 Stream-aligned&lt;/strong&gt;(对应&amp;quot;业务域&amp;quot;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Platform team 不是万能的&lt;/strong&gt;——平台做得太重会变成新的&amp;quot;巨石服务&amp;quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Complicated-subsystem team 的边界要稳&lt;/strong&gt;,不能随便拆——风控算法不会因为组织调整而&amp;quot;换语言&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="15-生产案例某物流公司从-8-个职能团队--4-个业务团队"&gt;1.5 生产案例:某物流公司从 8 个职能团队 → 4 个业务团队
&lt;/h3&gt;&lt;p&gt;我 2022 年辅导过的一家物流公司,典型康威定律反例:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;原组织(8 个职能团队)&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Java 组 8 人、DBA 组 3 人、前端组 6 人、测试组 4 人、运维组 5 人、产品组 7 人、UI 组 3 人、数据组 4 人&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;系统架构&lt;/strong&gt;:&amp;ldquo;前端-后端-DBA&amp;quot;三层单体,JSP + Spring MVC + 单库&lt;/li&gt;
&lt;li&gt;痛点:改一个&amp;quot;运单状态变更&amp;quot;需求要走 6 个团队、3 周排期&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;新组织(4 个 Stream-aligned + 2 个支撑)&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;运单业务团队 7 人(含 1 个后端、1 个前端、1 个测试、1 个产品)—— 端到端负责&amp;quot;运单&amp;rdquo;&lt;/li&gt;
&lt;li&gt;调度业务团队 6 人(同上结构)—— 端到端负责&amp;quot;调度&amp;quot;&lt;/li&gt;
&lt;li&gt;司机业务团队 5 人 —— 端到端负责&amp;quot;司机 + 车辆&amp;quot;&lt;/li&gt;
&lt;li&gt;财务业务团队 4 人 —— 端到端负责&amp;quot;对账 + 结算&amp;quot;&lt;/li&gt;
&lt;li&gt;平台团队 8 人 —— 提供 K8s + 监控 + CI/CD 自助平台&lt;/li&gt;
&lt;li&gt;赋能团队 3 人 —— 安全 + 可观测性&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;结果(6 个月后)&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每个 Stream-aligned 团队&lt;strong&gt;自己决定技术栈、自己决定发布节奏&lt;/strong&gt;——运单团队用 Java、调度团队用 Go、司机团队用 Node.js 都可以&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;部署频率从 1 次/周 → 30 次/天&lt;/strong&gt;(从 CI/CD 平台拿指标)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MTTR(平均恢复时间)从 4 小时 → 15 分钟&lt;/strong&gt;(因为业务团队对代码全栈负责,排查不需要跨团队)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;改一个&amp;quot;运单&amp;quot;需求,从 3 周 → 2 天&lt;/strong&gt;(一个团队内部闭环)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;这个案例的关键不是&amp;quot;用了微服务&amp;quot;,而是&amp;quot;先改了组织,后改的架构&amp;quot;&lt;/strong&gt;。如果只改架构不改组织,38 个微服务大泥球的悲剧会再上演。&lt;/p&gt;
&lt;h3 id="16-启示拆分前先问-3-个问题"&gt;1.6 启示:拆分前先问 3 个问题
&lt;/h3&gt;&lt;p&gt;回到我自己 2021 年的处境,看完 Team Topologies 后,我在拆分前先问了 3 个问题:&lt;/p&gt;
&lt;ol&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; —— 按业务域组建 4-5 个 Stream-aligned 团队,而不是按&amp;quot;前端/后端/DBA&amp;quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;业务边界 ≠ 团队边界时,谁先动?&lt;/strong&gt; —— &lt;strong&gt;永远是组织先动&lt;/strong&gt;。架构师单方面&amp;quot;按业务拆服务&amp;quot;而不调组织,会变成&amp;quot;38 个微服务大泥球&amp;quot;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这一节建立的认知是:&lt;strong&gt;服务拆分不是&amp;quot;技术活&amp;quot;,是&amp;quot;组织工程&amp;quot;&lt;/strong&gt;。下一节讲的 DDD,则是&amp;quot;怎么识别业务边界&amp;quot;的方法论工具箱。&lt;/p&gt;
&lt;h2 id="二领域驱动设计战术工具箱"&gt;二、领域驱动设计:战术工具箱
&lt;/h2&gt;
 &lt;blockquote&gt;
 &lt;p&gt;上一节讲了&amp;quot;为什么拆&amp;quot;(康威定律、组织先行)。这一节讲&amp;quot;按什么拆&amp;quot;——DDD 给的战术工具。
战略设计:识别业务边界(限界上下文)
战术设计:在一个上下文内怎么写代码(实体/值对象/聚合根/领域服务)&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;Eric Evans 2003 年的《Domain-Driven Design》中文版叫《领域驱动设计》,核心思想:&lt;strong&gt;软件的核心是它所服务的领域(domain),技术是手段&lt;/strong&gt;。DDD 不是一套框架,是一套思维方式 + 一组战术工具,&lt;strong&gt;专门解决&amp;quot;复杂业务怎么拆分&amp;quot;这个问题&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;我把 DDD 工具箱按&amp;quot;先用、后用&amp;quot;分成两层:**战略层(限界上下文)**先识别业务边界,**战术层(实体/值对象/聚合根)**再在一个上下文内写代码。&lt;/p&gt;
&lt;h3 id="21-战略层限界上下文bounded-context"&gt;2.1 战略层:限界上下文(Bounded Context)
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;限界上下文是 DDD 最重要的概念,没有之一&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;定义:一个限界上下文 = 一个业务领域 + 一套统一语言 + 一个代码边界。在限界上下文内部,术语含义是确定的(比如&amp;quot;订单&amp;quot;特指电商订单);跨限界上下文时,术语含义可能不同(比如&amp;quot;账户&amp;quot;在用户中心指&amp;quot;用户登录账号&amp;quot;,在支付中心指&amp;quot;钱包账户&amp;quot;)。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;实战:电商系统的限界上下文切分&lt;/strong&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;order-service / cart-service / promotion-service&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;pay-service / refund-service / wallet-service&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;库存上下文&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;SKU/库存/预占&lt;/td&gt;
					&lt;td&gt;库存数量管理、预占/释放&lt;/td&gt;
					&lt;td&gt;inventory-service / warehouse-service&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;logistics-service / dispatch-service&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;user-service / address-service / member-service&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;coupon-service / points-service / campaign-service&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;关键判断:为什么&amp;quot;订单&amp;quot;和&amp;quot;支付&amp;quot;必须分两个上下文?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;因为它们的&amp;quot;核心语言&amp;quot;不同:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;订单上下文里&amp;quot;订单&amp;quot; = 一份&lt;strong&gt;意图&lt;/strong&gt;(&amp;ldquo;我想买 X 商品,共 100 元&amp;rdquo;)&lt;/li&gt;
&lt;li&gt;支付上下文里&amp;quot;交易&amp;quot; = 一笔&lt;strong&gt;事实&lt;/strong&gt;(&amp;ldquo;我已支付 100 元,支付渠道是支付宝&amp;rdquo;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;订单和支付通过&lt;strong&gt;领域事件&lt;/strong&gt;(OrderCreated、PaymentCompleted)&lt;strong&gt;异步解耦&lt;/strong&gt;,而不是订单服务直接调支付服务同步收钱。这种&amp;quot;异步 + 事件&amp;quot;的设计,是微服务拆分后&lt;strong&gt;避免分布式事务&lt;/strong&gt;的第一道防线。&lt;/p&gt;
&lt;h3 id="22-战术层-1实体entity-vs-值对象value-object"&gt;2.2 战术层 1:实体(Entity) vs 值对象(Value Object)
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;实体&lt;/strong&gt;:有唯一标识(ID)、生命周期可变、状态可变的对象。例:&lt;code&gt;Order&lt;/code&gt;(&lt;code&gt;orderId=20240101001&lt;/code&gt;)、&lt;code&gt;User&lt;/code&gt;(&lt;code&gt;userId=10086&lt;/code&gt;)。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;值对象&lt;/strong&gt;:没有唯一标识、不可变、只描述属性的对象。例:&lt;code&gt;Address&lt;/code&gt;(地址)由&amp;quot;省/市/区/详细地址&amp;quot;组成,两个完全相同的地址可以互相替换;&lt;code&gt;Money&lt;/code&gt;(金额)由&amp;quot;数字 + 货币类型&amp;quot;组成,5 元和 5 元是等价的。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;实战对比&lt;/strong&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// ❌ 反例:用实体表示地址(过度设计)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@Entity&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Address&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Id&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Long&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 地址有自己的 ID&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;province&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// ... getter/setter&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// ✅ 正例:用值对象表示地址(不可变,按值相等)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@Value&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@Builder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;toBuilder&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Address&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;province&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 省&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 市&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;district&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 区&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 详细地址&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 值对象的好处:User 修改地址时,直接 setAddress(new Address(...))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 不用考虑&amp;#34;旧地址对象要不要清空引用&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;判断口诀&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;有 ID 吗?有 → &lt;strong&gt;实体&lt;/strong&gt;;没有 → 值对象&lt;/li&gt;
&lt;li&gt;可变吗?可变 → 实体;不可变 → 值对象&lt;/li&gt;
&lt;li&gt;生命周期独立吗?独立 → 实体;依附于其他对象 → 值对象&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="23-战术层-2聚合根aggregate-root--ddd-最难也最重要的概念"&gt;2.3 战术层 2:聚合根(Aggregate Root) — DDD 最难也最重要的概念
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;聚合根 = 一组紧密相关的实体 + 值对象的&amp;quot;管理者&amp;quot;&lt;/strong&gt;,外部访问聚合内部的实体,只能通过聚合根。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;经典案例:订单聚合&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Order (聚合根)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── OrderItem (实体) — 订单项
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ ├── SkuId
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ ├── Quantity
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ └── UnitPrice
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── ShippingAddress (值对象) — 收货地址
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── Buyer (值对象) — 买家信息
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;└── OrderStatus (枚举) — 订单状态
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;聚合根的不变式(invariant)&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;订单必须有 1 个或多个 OrderItem,不能有 0 个&lt;/li&gt;
&lt;li&gt;订单的总金额 = 所有 OrderItem 的数量 × 单价 之和(永远一致)&lt;/li&gt;
&lt;li&gt;订单创建后,收货地址不能修改(可以修改前 cancel + recreate)&lt;/li&gt;
&lt;li&gt;订单状态转换有规则:CREATED → PAID → SHIPPED → COMPLETED,不能跳过&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些规则&lt;strong&gt;只在聚合根 Order 内执行&lt;/strong&gt;,外部服务(支付、物流)不能直接改 OrderItem,只能通过 Order 暴露的方法(order.pay()、order.ship())间接修改。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;实战代码(Spring Boot + JPA)&lt;/strong&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;span class="lnt"&gt;38
&lt;/span&gt;&lt;span class="lnt"&gt;39
&lt;/span&gt;&lt;span class="lnt"&gt;40
&lt;/span&gt;&lt;span class="lnt"&gt;41
&lt;/span&gt;&lt;span class="lnt"&gt;42
&lt;/span&gt;&lt;span class="lnt"&gt;43
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@Entity&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@Table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;t_order&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Id&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 聚合根内部包含实体和值对象&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@OneToMany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cascade&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CascadeType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ALL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;orphanRemoval&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fetch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FetchType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;EAGER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@JoinColumn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;order_id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OrderItem&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Embedded&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ShippingAddress&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;shippingAddress&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Enumerated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EnumType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;STRING&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OrderStatus&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 不变式:通过聚合根的方法保证&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;addItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SkuId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;skuId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Quantity&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;qty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Money&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;unitPrice&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OrderStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;CREATED&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;IllegalStateException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;只有 CREATED 状态的订单能加商品&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OrderItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;skuId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;qty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;unitPrice&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Money&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;totalAmount&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getUnitPrice&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;multiply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getQuantity&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Money&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ZERO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Money&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;pay&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OrderStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;CREATED&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;IllegalStateException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;只有 CREATED 状态的订单能支付&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;IllegalStateException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;空订单不能支付&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OrderStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;PAID&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 领域事件:发布 OrderPaid 事件&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;registerEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OrderPaidEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;totalAmount&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;聚合根的&amp;quot;事务边界&amp;quot;价值&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;一个聚合根 = 一个事务边界。&lt;strong&gt;改一个聚合根内的多个实体,在同一个数据库事务里&lt;/strong&gt;(因为它们物理上在同一张表或同一组表)。&lt;strong&gt;跨聚合根的修改,必须用最终一致性(领域事件)&lt;/strong&gt;,不能用分布式事务(2PC/Saga)。&lt;/p&gt;
&lt;p&gt;这是拆分微服务后&lt;strong&gt;最关键的事务策略&lt;/strong&gt;——同一个微服务内部的多个表 = 强一致性(本地事务);跨微服务的数据 = 最终一致性(事件 + 对账)。&lt;/p&gt;
&lt;h3 id="24-战术层-3领域服务domain-service-vs-应用服务application-service"&gt;2.4 战术层 3:领域服务(Domain Service) vs 应用服务(Application Service)
&lt;/h3&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;Order.pay()、User.changePassword()&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;PriceCalculator(算价格,跨 OrderItem + Promotion)&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;OrderApplicationService.createOrder()&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;基础设施&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;数据库、消息、缓存、RPC&lt;/td&gt;
					&lt;td&gt;有&lt;/td&gt;
					&lt;td&gt;OrderRepository、EventPublisher&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;实战案例:下单流程&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// ✅ 应用服务:编排用例(不写业务规则)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@Service&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderApplicationService&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Autowired&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OrderRepository&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;orderRepository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Autowired&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PriceCalculator&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;priceCalculator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 领域服务&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Autowired&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;InventoryService&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;inventoryService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 外部服务&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Autowired&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;EventPublisher&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eventPublisher&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Transactional&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;createOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CreateOrderCommand&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 1. 创建订单(聚合根内部的不变式)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getBuyer&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getShippingAddress&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 2. 调领域服务算价格&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OrderItemCommand&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getItems&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Money&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;unitPrice&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;priceCalculator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;calculate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSkuId&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getQuantity&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSkuId&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getQuantity&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;unitPrice&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 3. 预占库存(跨聚合根,走最终一致性)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;inventoryService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;reserve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getOrderId&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getItems&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 4. 保存&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;orderRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 5. 发布领域事件&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getDomainEvents&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;eventPublisher&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;判断口诀&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;里&lt;/li&gt;
&lt;li&gt;逻辑涉及&amp;quot;调用外部系统、事务边界、事件发布&amp;quot;?→ 写在&lt;strong&gt;应用服务&lt;/strong&gt;里&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="25-战术层-4领域事件domain-event"&gt;2.5 战术层 4:领域事件(Domain Event)
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;领域事件 = 业务上已经发生的事实&lt;/strong&gt;。&amp;ldquo;OrderCreated&amp;quot;是事实,&amp;ldquo;CreateOrderCommand&amp;quot;是意图(还没发生)。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;事件结构&lt;/strong&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderPaidEvent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Money&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Instant&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;paidAt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// getter...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;事件命名&lt;/strong&gt;:用&lt;strong&gt;过去时态&lt;/strong&gt;(&lt;code&gt;OrderPaid&lt;/code&gt;、&lt;code&gt;PaymentCompleted&lt;/code&gt;、&lt;code&gt;InventoryReserved&lt;/code&gt;),不要用&amp;quot;动作时态&amp;rdquo;(&lt;code&gt;CreateOrder&lt;/code&gt; 这种是命令不是事件)。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;实战消费:支付完成后,触发物流 + 积分 + 通知三个动作&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 支付服务发布事件&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@EventPublisher&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PaymentApplicationService&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;handlePayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PaymentCommand&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// ... 处理支付 ...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eventPublisher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PaymentCompletedEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getOrderId&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAmount&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 物流服务订阅&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@EventHandler&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LogisticsEventHandler&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PaymentCompletedEvent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 创建运单、调度配送&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;shipmentService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createShipment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getOrderId&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 积分服务订阅&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@EventHandler&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PointsEventHandler&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PaymentCompletedEvent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 加积分&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pointsService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addPoints&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getBuyerId&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAmount&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;关键设计原则&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;(支付服务不等物流服务完成)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;事件要有版本号&lt;/strong&gt;(&lt;code&gt;v1&lt;/code&gt;、&lt;code&gt;v2&lt;/code&gt;),演化时兼容老版本&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;事件持久化&lt;/strong&gt;到消息中间件(Kafka/RocketMQ),消费者崩溃可重放&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="26-上下文映射context-map跨上下文怎么协作"&gt;2.6 上下文映射(Context Map):跨上下文怎么协作
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;当限界上下文确定后,跨上下文怎么交互?这就是上下文映射要解决的问题。&lt;/strong&gt; 常见 9 种模式(选最常见的 4 种):&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;Partnership&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;Customer-Supplier&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;Anti-Corruption Layer&lt;/strong&gt;(防腐层)&lt;/td&gt;
					&lt;td&gt;翻译老系统的&amp;quot;烂接口&amp;rdquo;&lt;/td&gt;
					&lt;td&gt;集成遗留 ERP 时加 ACL&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Shared Kernel&lt;/strong&gt;(共享内核)&lt;/td&gt;
					&lt;td&gt;两个上下文共享一小段代码&lt;/td&gt;
					&lt;td&gt;共享 &lt;code&gt;Money&lt;/code&gt; 值对象&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;ACL(防腐层)实战:对接老 ERP 系统&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;老 ERP 系统的接口是这样的(典型的&amp;quot;大泥球&amp;quot;对外暴露):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 老 ERP 的返回结构(各种字段混在一起)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ErpLegacyResponse&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;order_id_erp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 下划线命名&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user_no&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sku_code&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;qty&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;price_rmb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 混币种&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// &amp;#34;01&amp;#34;/&amp;#34;02&amp;#34;/&amp;#34;99&amp;#34; 这种魔数&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;memo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 万能字段&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// ... 200 多个字段&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;不写 ACL 的下场&lt;/strong&gt;:订单服务里全是 &lt;code&gt;if (status.equals(&amp;quot;01&amp;quot;)) { ... } else if (status.equals(&amp;quot;02&amp;quot;)) { ... }&lt;/code&gt; 这种魔数判断,改一次 ERP 字段全崩。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;写 ACL 的实战&lt;/strong&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;span class="lnt"&gt;38
&lt;/span&gt;&lt;span class="lnt"&gt;39
&lt;/span&gt;&lt;span class="lnt"&gt;40
&lt;/span&gt;&lt;span class="lnt"&gt;41
&lt;/span&gt;&lt;span class="lnt"&gt;42
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 1. 在订单服务里定义自己的领域模型&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OrderId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OrderStatus&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 枚举:PAID / SHIPPED / COMPLETED&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Money&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;totalAmount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// ...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 2. ACL 把老 ERP 的&amp;#34;魔数&amp;#34;翻译成领域枚举&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@Component&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ErpAntiCorruptionLayer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;translate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ErpLegacyResponse&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;erp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OrderId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;erp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getOrder_id_erp&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;translateStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;erp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getStatus_code&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Money&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BigDecimal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;valueOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;erp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getPrice_rmb&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Currency&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;CNY&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// ... 字段映射&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OrderStatus&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;translateStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;erpCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;switch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;erpCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;01&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OrderStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;PAID&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;02&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OrderStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SHIPPED&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;99&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OrderStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;COMPLETED&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;IllegalArgumentException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;未知 ERP 状态: &amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;erpCode&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 3. 订单服务的其他代码只看 Order,不接触 ErpLegacyResponse&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@Service&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderSyncService&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Autowired&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ErpAntiCorruptionLayer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;acl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;syncFromErp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;erpOrderId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ErpLegacyResponse&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;erp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;erpClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;erpOrderId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;acl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;translate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;erp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// ACL 翻译&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;orderRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 业务代码只看 Order&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;ACL 是微服务拆分中&lt;strong&gt;最被低估的设计&lt;/strong&gt;。每当你要&amp;quot;集成老系统&amp;quot;&amp;ldquo;对接外部第三方&amp;quot;&amp;ldquo;统一多个数据源&amp;quot;时,&lt;strong&gt;先想 ACL&lt;/strong&gt;——不要让外部的脏数据污染你的领域模型。&lt;/p&gt;
&lt;h3 id="27-事件风暴event-stormingddd-的工作坊方法"&gt;2.7 事件风暴(Event Storming):DDD 的工作坊方法
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;事件风暴是 Alberto Brandolini 2013 年提出的 DDD 工作坊方法&lt;/strong&gt;,核心是用&amp;quot;事件&amp;quot;驱动团队讨论业务,最终识别出限界上下文、聚合根、领域事件。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15 步流程(简化版,1-2 天)&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;邀请业务专家 + 开发 + 测试 + 运维&lt;/strong&gt;(全栈人员,10 人左右)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;准备一面大白墙 + 橙色便签(领域事件) + 蓝色便签(命令) + 黄色便签(外部系统) + 紫色便签(问题)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;从业务流程的&amp;quot;起点&amp;quot;开始&lt;/strong&gt;(如&amp;quot;用户下单&amp;rdquo;),贴第 1 个事件便签:&lt;code&gt;OrderCreated&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;依次向后推演&lt;/strong&gt;:用户支付 → &lt;code&gt;PaymentCompleted&lt;/code&gt;,库存预占 → &lt;code&gt;InventoryReserved&lt;/code&gt;,发货 → &lt;code&gt;ShipmentDispatched&lt;/code&gt;,签收 → &lt;code&gt;ShipmentDelivered&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;每个事件前面贴一个命令&lt;/strong&gt;:用户想下单 → &lt;code&gt;CreateOrder&lt;/code&gt;,支付 → &lt;code&gt;PayOrder&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;事件之间识别&amp;quot;聚合根&amp;rdquo;&lt;/strong&gt;:OrderCreated + OrderPaid + OrderShipped + OrderCompleted 围绕 &lt;code&gt;Order&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;聚合根按业务相关性归类 → 限界上下文&lt;/strong&gt;:Order、Pay、Inventory 三个聚合根 → 销售上下文&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;画上下文边界&lt;/strong&gt;:不同上下文用不同颜色便签区分&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;识别上下文映射&lt;/strong&gt;:Order → Inventory(下游消费)、Order → Pay(下游消费)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;补充遗漏&lt;/strong&gt;:贴紫色便签写&amp;quot;未解决的问题&amp;quot;&amp;ldquo;模糊的业务规则&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;优先级排序&lt;/strong&gt;:最重要的 1-2 个上下文先实施&lt;/li&gt;
&lt;/ol&gt;
&lt;p&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;和&amp;quot;业务想当然&amp;quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可视化产出物&lt;/strong&gt;就是微服务的拆分依据&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;一次事件风暴的结果能用 6-12 个月&lt;/strong&gt;——边界识别后,微服务边界、API 边界、数据库边界都按这个走&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我那次 38 个微服务大泥球重构,关键的&amp;quot;销售上下文/支付上下文/库存上下文&amp;quot;划分,就是 2 天事件风暴的产物。&lt;strong&gt;没有一个架构师能独自想清楚业务边界,必须业务专家+开发+测试一起推演&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id="三微服务拆分-5-条工业级原则"&gt;三、微服务拆分 5 条工业级原则
&lt;/h2&gt;
 &lt;blockquote&gt;
 &lt;p&gt;上一节讲了 DDD 工具箱(限界上下文、聚合根、领域事件、ACL、事件风暴),这一节讲把这套工具落到&amp;quot;代码 + 数据库 + 团队&amp;quot;上的 5 条工业级原则。
这 5 条不是教条,是&amp;quot;踩过 38 个大泥球&amp;quot;后归纳的&amp;quot;高概率正确&amp;quot;经验。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h3 id="31-原则-1单一业务职责one-bounded-context-one-service"&gt;3.1 原则 1:单一业务职责(One Bounded Context, One Service)
&lt;/h3&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;strong&gt;单体服务的微服务外壳&lt;/strong&gt;,依然是大泥球。看起来是微服务,实际部署 1 次要协调 4 个业务的发布窗口,本质还是单体。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;正例&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;订单服务:只管 Order 聚合(订单 + 订单项)&lt;/li&gt;
&lt;li&gt;支付服务:只管 Payment 聚合(交易 + 退款)&lt;/li&gt;
&lt;li&gt;库存服务:只管 Inventory 聚合(SKU 库存 + 预占)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;判断标准:用&amp;quot;如果改这个服务,需要不需要协调其他业务团队&amp;quot;作为标尺&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;&lt;/li&gt;
&lt;li&gt;改一个服务,只影响 1-2 个聚合根 → &lt;strong&gt;可能拆得太细&lt;/strong&gt;(每个聚合根一个服务是过度拆分)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;&amp;ldquo;拆得太细&amp;quot;的反模式&lt;/strong&gt;:&lt;code&gt;Order&lt;/code&gt;、&lt;code&gt;OrderItem&lt;/code&gt;、&lt;code&gt;OrderAddress&lt;/code&gt; 三个服务(订单项、地址独立成服务)。问题是:OrderItem 离开 Order 没意义,OrderAddress 离开 Order 也没意义,&lt;strong&gt;它们的生命周期和 Order 完全绑定&lt;/strong&gt;,这种&amp;quot;拆&amp;quot;只是把本地事务变成了分布式事务,徒增复杂度。&lt;/p&gt;
&lt;h3 id="32-原则-2边界自治autonomy--数据库隔离是底线"&gt;3.2 原则 2:边界自治(Autonomy) — 数据库隔离是底线
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;口诀:一个微服务 = 一套独立的数据库(Schema/DB)&lt;/strong&gt;,不共享表、不共享连接池、不跨服务 JOIN。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;反例(38 个大泥球的核心病灶)&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;38 个服务都连同一个 MySQL 集群&lt;/li&gt;
&lt;li&gt;订单服务 &lt;code&gt;SELECT * FROM t_user WHERE user_id = ?&lt;/code&gt; 跨服务读用户表&lt;/li&gt;
&lt;li&gt;支付服务 &lt;code&gt;UPDATE t_inventory SET stock = stock - 1&lt;/code&gt; 跨服务改库存&lt;/li&gt;
&lt;li&gt;一旦 DBA 把 &lt;code&gt;t_user&lt;/code&gt; 改个字段名,38 个服务全挂&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;正例&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;订单库 &lt;code&gt;t_order&lt;/code&gt;、&lt;code&gt;t_order_item&lt;/code&gt;(订单库独享)&lt;/li&gt;
&lt;li&gt;支付库 &lt;code&gt;t_payment&lt;/code&gt;、&lt;code&gt;t_refund&lt;/code&gt;(支付库独享)&lt;/li&gt;
&lt;li&gt;库存库 &lt;code&gt;t_inventory&lt;/code&gt;、&lt;code&gt;t_reservation&lt;/code&gt;(库存库独享)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;跨服务要数据,只能通过 API 或事件&lt;/strong&gt;,不能直接 SQL 查&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;实战:订单服务要查用户地址怎么办?&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// ❌ 反例:跨库 JOIN&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@Query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;SELECT o.*, a.detail FROM t_order o JOIN t_user_address a ON o.user_id = a.user_id WHERE o.order_id = ?&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;OrderDetail&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;findDetail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// ✅ 正例:通过 OpenFeign 调用用户服务&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@FeignClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;user-service&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;UserServiceClient&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;/api/v1/users/{userId}/addresses/{addressId}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AddressDTO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;getAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@PathVariable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Long&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@PathVariable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Long&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;addressId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@Service&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderQueryService&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Autowired&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;UserServiceClient&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;userClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OrderDetail&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;getDetail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;orderRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="na"&gt;orElseThrow&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AddressDTO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;userClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getBuyerId&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAddressId&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OrderDetail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;为什么&amp;quot;数据库隔离&amp;quot;这么重要?因为它是微服务&amp;quot;可独立演进&amp;quot;的技术底线&lt;/strong&gt;。共享数据库时,改一个表要协调所有用的服务;隔离后,改库 = 改当前服务,不用协调别人。&lt;/p&gt;
&lt;h3 id="33-原则-3独立演进independent-evolution--接口稳定内部可改"&gt;3.3 原则 3:独立演进(Independent Evolution) — 接口稳定、内部可改
&lt;/h3&gt;&lt;p&gt;**口诀:微服务的 API 接口(对外暴露的 OpenAPI/gRPC IDL)**一旦发布,&lt;strong&gt;就尽量不改&lt;/strong&gt;;&lt;strong&gt;实现细节&lt;/strong&gt;(代码、数据库、缓存)可以随时重构。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;反例(常见的&amp;quot;服务失控&amp;rdquo;)&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;支付服务 V1 接口 &lt;code&gt;POST /api/pay&lt;/code&gt; 返回 &lt;code&gt;{ &amp;quot;payId&amp;quot;: &amp;quot;P001&amp;quot; }&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;3 个月后产品改需求,支付服务改返回 &lt;code&gt;{ &amp;quot;transactionId&amp;quot;: &amp;quot;P001&amp;quot; }&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;订单服务 27 个调用方全挂&lt;/li&gt;
&lt;li&gt;支付团队为了不挂调用方,把&amp;quot;transactionId&amp;quot; 改回 &amp;ldquo;payId&amp;rdquo; 但内部多加一个&amp;quot;transactionId&amp;quot; 字段(双写)——&lt;strong&gt;API 越来越乱,最终没人敢动&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;正例:版本化 API + 内部实现可改&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;支付 V1:&lt;code&gt;POST /api/v1/pay&lt;/code&gt; 返回 &lt;code&gt;{ &amp;quot;payId&amp;quot;: &amp;quot;P001&amp;quot; }&lt;/code&gt; —— &lt;strong&gt;永久保留,标记 deprecated&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;支付 V2:&lt;code&gt;POST /api/v2/pay&lt;/code&gt; 返回 &lt;code&gt;{ &amp;quot;transactionId&amp;quot;: &amp;quot;P001&amp;quot; }&lt;/code&gt; —— &lt;strong&gt;新接口&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;订单服务新代码用 V2,老代码保留 V1(并设 6 个月废弃时间表)&lt;/li&gt;
&lt;li&gt;支付服务&lt;strong&gt;内部实现可改&lt;/strong&gt;:V1/V2 共享同一个 PaymentAppService,只是 Controller 层路由不同&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;&amp;ldquo;接口稳定&amp;quot;的额外保障&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用 &lt;strong&gt;OpenAPI 3.0&lt;/strong&gt; 规范写接口(代码生成,人写容易漏)&lt;/li&gt;
&lt;li&gt;改接口前&lt;strong&gt;先在 API 仓库发 RFC&lt;/strong&gt;,所有调用方在 PR 里 ack&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;没有 ack 的 PR 不能合 master&lt;/strong&gt;(流程强制)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;&amp;ldquo;内部可改&amp;quot;的边界&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;改一个服务的代码 → 这个服务的 owner 团队自己决定&lt;/li&gt;
&lt;li&gt;改数据库表结构 → 同上&lt;/li&gt;
&lt;li&gt;改 API 契约 → &lt;strong&gt;必须走 RFC 流程&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="34-原则-4可观测observability--拆不开就先建观测"&gt;3.4 原则 4:可观测(Observability) — 拆不开就先建观测
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;口诀:在拆之前先把&amp;quot;链路追踪 + 统一日志 + 指标监控&amp;quot;建好,否则一拆就崩&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;我见过很多团队&lt;strong&gt;直接拆 38 个服务,但没建观测系统&lt;/strong&gt;,结果:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一个请求跨 8 个服务,出问题不知道卡在哪&lt;/li&gt;
&lt;li&gt;18 个服务都在打 ERROR 日志,但日志格式不统一,grep 不到&lt;/li&gt;
&lt;li&gt;数据库慢查询没法定位是哪个服务发的&lt;/li&gt;
&lt;li&gt;2 周后 38 个服务重新合并回 1 个单体(因为没人能维护)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;可观测的 3 大支柱&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;链路追踪(Tracing)&lt;/strong&gt;:一个请求的全链路调用图,看到每个服务的耗时、错误
&lt;ul&gt;
&lt;li&gt;工具:Jaeger、Zipkin、SkyWalking&lt;/li&gt;
&lt;li&gt;关键字段:traceId(全局唯一)、spanId(单服务内唯一)、parentSpanId(上游)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;统一日志(Logging)&lt;/strong&gt;:结构化日志(JSON 格式),带 traceId 串联
&lt;ul&gt;
&lt;li&gt;工具:ELK(Elasticsearch + Logstash + Kibana)、Loki&lt;/li&gt;
&lt;li&gt;关键字段:traceId、serviceName、level、message、timestamp&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;指标监控(Metrics)&lt;/strong&gt;:服务的 QPS、错误率、延迟 P99、JVM/DB 连接池
&lt;ul&gt;
&lt;li&gt;工具:Prometheus + Grafana&lt;/li&gt;
&lt;li&gt;关键指标:&lt;strong&gt;RED 方法&lt;/strong&gt;(Rate 请求数 / Error 错误数 / Duration 延迟)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;生产案例:38 个大泥球重构前的&amp;quot;先建观测&amp;rdquo;&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;第一周:全员接入 SkyWalking(零业务代码改动,只加 Java agent)&lt;/li&gt;
&lt;li&gt;第二周:把 27 个服务的日志改成 JSON 格式,接 ELK&lt;/li&gt;
&lt;li&gt;第三周:Prometheus + Grafana 上线,所有服务的 QPS / 错误率 / 延迟可视化&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;第四周开始拆服务&lt;/strong&gt;——这时的可观测是&amp;quot;边拆边看&amp;rdquo;,不是&amp;quot;先拆再补&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;反观测的反模式&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;服务 A 用 Logback 写文本日志,服务 B 用 Log4j2 写 JSON 日志,服务 C 直接 &lt;code&gt;System.out.println&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;每个服务有自己的&amp;quot;日志格式&amp;quot;,运维同学想找一条 ERROR,要用 3 种不同的 grep&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;建观测的第一件事就是统一日志格式&lt;/strong&gt;(建议 SLF4J + Logback + JSON Encoder)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="35-原则-5数据收敛data-convergence--每个服务管自己的数据"&gt;3.5 原则 5:数据收敛(Data Convergence) — 每个服务管自己的数据
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;口诀:一个服务 = 一份权威数据源(Source of Truth),其他服务要这个数据,只能&amp;quot;订阅&amp;quot;或&amp;quot;调用&amp;quot;&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;反例(数据散落是分布式系统的&amp;quot;癌症&amp;quot;)&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用户名&amp;quot;张三&amp;quot;在用户表、订单表、支付表、物流表里都有一份&lt;/li&gt;
&lt;li&gt;用户改了昵称&amp;quot;张三&amp;quot; → &amp;ldquo;Zhang San&amp;rdquo;,只有用户表更新&lt;/li&gt;
&lt;li&gt;订单表、物流表、支付表里还是&amp;quot;张三&amp;quot;&lt;/li&gt;
&lt;li&gt;用户看到订单详情里&amp;quot;买家:张三&amp;quot;,但个人中心显示&amp;quot;Zhang San&amp;quot;——&lt;strong&gt;数据不一致&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;正例:事件驱动的数据同步&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 用户服务:用户改昵称,发布 UserProfileChanged 事件&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@EventPublisher&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserApplicationService&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;changeNickname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UserId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;newNickname&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;userRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="na"&gt;orElseThrow&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;changeNickname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newNickname&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;userRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eventPublisher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;UserProfileChangedEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;newNickname&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 订单服务:订阅 UserProfileChanged,更新自己表里的冗余字段&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@EventHandler&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderEventHandler&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UserProfileChangedEvent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 异步更新订单表里的冗余字段&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;orderRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;updateBuyerNickname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getUserId&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getNewNickname&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;&amp;ldquo;数据收敛&amp;quot;的关键设计&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;唯一权威源&lt;/strong&gt;:用户的&amp;quot;昵称&amp;quot;只在用户表里是权威,其他表是&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;容忍延迟&lt;/strong&gt;:下游查询要容忍&amp;quot;短暂不一致&amp;rdquo;——比如订单详情显示&amp;quot;张三&amp;quot;(用户表已变&amp;quot;Zhang San&amp;quot;,事件还没到),用户看到的不是最新昵称&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;为什么不能&amp;quot;实时同步&amp;quot;&lt;/strong&gt;(同步双写)?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用户改昵称 → 同步调 27 个服务的更新接口&lt;/li&gt;
&lt;li&gt;一个服务慢 5 秒,用户就卡 5 秒&lt;/li&gt;
&lt;li&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;数据收敛的 4 个层次(选你需要的)&lt;/strong&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;L1 - 强一致(本地事务)&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;L2 - 最终一致(领域事件)&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;L3 - 异步对账(批处理)&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;跨服务数据校验&lt;/td&gt;
					&lt;td&gt;T+1 对账、库存盘点&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;L4 - 妥协查询(直接跨库)&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;&lt;strong&gt;反模式:用分布式事务(Seata AT 模式)代替事件&lt;/strong&gt;——所有跨服务都 Seata 一把梭哈,看似简单,实际&lt;strong&gt;性能极差 + 耦合极重&lt;/strong&gt;,任何服务挂都拖垮全局。Seata 在第 14 篇会细讲,这里只说:&lt;strong&gt;优先事件 + 最终一致,只在&amp;quot;必须强一致&amp;quot;的核心金融场景用 Seata&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id="四生产场景-6-个真实案例"&gt;四、生产场景 6 个真实案例
&lt;/h2&gt;
 &lt;blockquote&gt;
 &lt;p&gt;前三节讲方法论(康威 + DDD + 5 原则),这一节讲 6 个真实的&amp;quot;拆分&amp;quot;案例。每个案例都给:背景、痛点、解决方案、结果、教训。
案例来源:3 个来自本人亲历(脱敏处理),3 个来自公开技术博客/技术大会。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h3 id="41-案例-1订单中台拆分--从巨型-order到-5-个领域服务"&gt;4.1 案例 1:订单中台拆分 — 从&amp;quot;巨型 Order&amp;quot;到 5 个领域服务
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;背景&lt;/strong&gt;:某 B2B 电商公司,2018 年起家的单体系统,所有业务(订单、库存、支付、促销、会员)都在一个 Spring Boot 应用的 &lt;code&gt;com.xx.order&lt;/code&gt; 包下,3 年后这个包代码量达到 &lt;strong&gt;67 万行&lt;/strong&gt;,部署时间 40 分钟。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;痛点&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;改一行订单代码,整个应用重新编译 + 部署&lt;/li&gt;
&lt;li&gt;任何一个小问题都要全量回滚,影响所有业务&lt;/li&gt;
&lt;li&gt;团队扩张到 25 人后,合并冲突每天 30+ 次&lt;/li&gt;
&lt;li&gt;QPS 撑不住双 11,只能堆机器(已堆到 80 台)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;拆分方案&lt;/strong&gt;(按 DDD 限界上下文):&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;order-service&lt;/td&gt;
					&lt;td&gt;Order + OrderItem&lt;/td&gt;
					&lt;td&gt;订单库 8 张表&lt;/td&gt;
					&lt;td&gt;订单团队 7 人&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;inventory-service&lt;/td&gt;
					&lt;td&gt;Inventory + Reservation&lt;/td&gt;
					&lt;td&gt;库存库 5 张表&lt;/td&gt;
					&lt;td&gt;库存团队 5 人&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;pay-service&lt;/td&gt;
					&lt;td&gt;Payment + Refund&lt;/td&gt;
					&lt;td&gt;支付库 4 张表&lt;/td&gt;
					&lt;td&gt;支付团队 5 人&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;promotion-service&lt;/td&gt;
					&lt;td&gt;Coupon + ActivityRule&lt;/td&gt;
					&lt;td&gt;营销库 6 张表&lt;/td&gt;
					&lt;td&gt;营销团队 4 人&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;member-service&lt;/td&gt;
					&lt;td&gt;Member + Address + Points&lt;/td&gt;
					&lt;td&gt;会员库 7 张表&lt;/td&gt;
					&lt;td&gt;会员团队 4 人&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;关键技术决策&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;绞杀者模式(Strangler Fig)&lt;/strong&gt;:新服务逐步替代老接口,而不是一次性&amp;quot;big bang&amp;quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;API 网关做路由&lt;/strong&gt;:老接口 &lt;code&gt;/api/legacy/order/*&lt;/code&gt; 路由到旧代码,新接口 &lt;code&gt;/api/v2/order/*&lt;/code&gt; 路由到新服务&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据双写期 3 个月&lt;/strong&gt;:老库新库同步写,读用开关切换,逐步放量&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;事件总线&lt;/strong&gt;:用 Kafka 做领域事件总线,5 个服务通过 Topic 解耦&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;结果(6 个月后)&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;部署时间从 40 分钟 → 单服务 2 分钟&lt;/li&gt;
&lt;li&gt;故障爆炸半径从 80 台机器 → 单服务 2-4 台&lt;/li&gt;
&lt;li&gt;团队独立发布,从 1 次/周 → 30 次/天&lt;/li&gt;
&lt;li&gt;双 11 QPS 峰值从 8000 → 35000(横向扩展更容易)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;教训&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;第一刀最难&lt;/strong&gt;——拆第一个服务(order-service)花了 4 个月,后面拆每个服务 1-2 个月&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据双写期一定要有&amp;quot;开关&amp;quot;&lt;/strong&gt;——不能&amp;quot;老库写、新库不写&amp;quot;,必须有双写开关和读开关,出问题秒级回滚&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;遗留的&amp;quot;老接口&amp;quot;不要立即删&lt;/strong&gt;——保留 6-12 个月,等所有调用方迁完再删&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="42-案例-2用户中心拆分--多端数据收敛"&gt;4.2 案例 2:用户中心拆分 — 多端数据收敛
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;背景&lt;/strong&gt;:某社交 App,用户数据散落在 5 个服务里(账号、资料、关系链、设置、设备),每个服务都有自己的 &lt;code&gt;t_user_xxx&lt;/code&gt; 表,改一个&amp;quot;昵称&amp;quot;字段要协调 5 个服务。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;痛点&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用户改昵称,5 个服务并发更新,经常 2-3 个成功 2-3 个失败,&lt;strong&gt;数据长期不一致&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&amp;ldquo;查看用户主页&amp;quot;要调 5 个服务的 API,任何一个慢,主页就慢&lt;/li&gt;
&lt;li&gt;新接入业务(如&amp;quot;创作者中心&amp;rdquo;)要复制一份用户数据,造成&amp;quot;数据散落&amp;quot;恶性循环&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;拆分方案&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;保留一个&lt;strong&gt;权威的 &lt;code&gt;user-core&lt;/code&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;code&gt;UserProfileChanged&lt;/code&gt; 事件传播,下游服务订阅并更新自己表的冗余字段&lt;/li&gt;
&lt;li&gt;设置、设备这种&amp;quot;用户私有的配置&amp;quot;下沉到 user-settings 服务,有自己的存储&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;关键代码&lt;/strong&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// user-core:权威源&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@Entity&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Long&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nickname&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;avatarUrl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// ... 只有这几个字段是&amp;#34;权威&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@EventPublisher&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;changeNickname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Long&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;newNickname&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;userRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="na"&gt;orElseThrow&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;oldNickname&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getNickname&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setNickname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newNickname&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;userRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 发布事件,所有订阅方异步更新冗余字段&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eventPublisher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;UserProfileChangedEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;oldNickname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;newNickname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Instant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 订单服务:订阅,更新冗余&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@EventHandler&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@Transactional&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UserProfileChangedEvent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;orderRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;updateBuyerSnapshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getUserId&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getNewNickname&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;结果&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用户改昵称:从 5 服务并发同步 → 1 服务本地事务 + 事件广播&lt;/li&gt;
&lt;li&gt;改昵称 P99 延迟:从 800ms → 80ms&lt;/li&gt;
&lt;li&gt;数据最终一致时间:通常 2-5 秒,最坏 30 秒&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2 年内 0 起&amp;quot;用户数据不一致&amp;quot;工单&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;教训&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;冗余是&amp;quot;成本&amp;quot;不是&amp;quot;问题&amp;rdquo;&lt;/strong&gt;——核心问题不是&amp;quot;有冗余&amp;quot;,而是&amp;quot;冗余没人维护&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;——每天 T+1 跑一次&amp;quot;权威源 vs 冗余&amp;quot;对比,发现不一致人工补&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="43-案例-3支付链路拆分--强弱一致的分层"&gt;4.3 案例 3:支付链路拆分 — 强弱一致的分层
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;背景&lt;/strong&gt;:某支付公司,核心是&amp;quot;用户充值 → 钱包账户 → 提现&amp;quot;链路。原架构是 1 个大的 pay-service,内部 5 个模块:账户、充值、提现、对账、风控。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;痛点&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;5 个模块耦合,改一个&amp;quot;提现限额&amp;quot;要重新部署整个 pay-service&lt;/li&gt;
&lt;li&gt;风控规则改 1 行,必须经过全套回归测试&lt;/li&gt;
&lt;li&gt;大促时充值并发高,把对账的 CPU 也吃光了&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;拆分方案&lt;/strong&gt;(按&amp;quot;强弱一致分层&amp;quot;原则):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;account-service&lt;/code&gt;:钱包账户(强一致,本地事务)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;recharge-service&lt;/code&gt;:充值(本地事务)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;withdraw-service&lt;/code&gt;:提现(本地事务)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;risk-service&lt;/code&gt;:风控规则(无状态,规则引擎)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;reconcile-service&lt;/code&gt;:对账(批处理,独立部署,资源隔离)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;关键设计&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;account-service&lt;/code&gt; 的余额修改用&lt;strong&gt;本地事务 + 行锁&lt;/strong&gt;(强一致,绝不能错)&lt;/li&gt;
&lt;li&gt;充值流程:用户 → &lt;code&gt;recharge&lt;/code&gt; → 调第三方支付 → 回调 → &lt;code&gt;recharge&lt;/code&gt; → &lt;strong&gt;发 &lt;code&gt;RechargeSuccess&lt;/code&gt; 事件&lt;/strong&gt; → &lt;code&gt;account&lt;/code&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;p&gt;&lt;strong&gt;结果&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;单服务 QPS 提升 5 倍(资源隔离,不再相互影响)&lt;/li&gt;
&lt;li&gt;风控规则改一行代码,30 秒上线(独立部署)&lt;/li&gt;
&lt;li&gt;大促期间 0 起&amp;quot;对账延迟&amp;quot;事故(批处理资源隔离)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;教训&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;不是所有支付场景都要&amp;quot;强一致&amp;quot;&lt;/strong&gt;——充值链路可以&amp;quot;最终一致 + 对账&amp;quot;,但钱包余额必须&amp;quot;强一致&amp;quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&amp;ldquo;分而治之&amp;quot;在金融场景特别重要&lt;/strong&gt;——把&amp;quot;实时链路&amp;quot;和&amp;quot;批处理链路&amp;quot;拆开,避免相互挤兑资源&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="44-案例-4数据一致性--事件总线的-4-道防线"&gt;4.4 案例 4:数据一致性 — 事件总线的 4 道防线
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;背景&lt;/strong&gt;:某零售公司,营销活动期间单日订单 50 万,涉及 12 个服务的协同,经常出现&amp;quot;订单已支付,库存没扣&amp;quot;&amp;ldquo;用户已退款,钱包没加&amp;quot;等不一致问题。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;痛点&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;事件丢失:Kafka 偶发重平衡,消费者没收到事件&lt;/li&gt;
&lt;li&gt;重复消费:网络抖动导致事件重发&lt;/li&gt;
&lt;li&gt;顺序错乱:用户退款事件比支付事件先到&lt;/li&gt;
&lt;li&gt;状态错位:服务 A 说&amp;quot;已发货&amp;rdquo;,服务 B 说&amp;quot;待支付&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;解决方案:事件总线的 4 道防线&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;span class="lnt"&gt;38
&lt;/span&gt;&lt;span class="lnt"&gt;39
&lt;/span&gt;&lt;span class="lnt"&gt;40
&lt;/span&gt;&lt;span class="lnt"&gt;41
&lt;/span&gt;&lt;span class="lnt"&gt;42
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 1. 持久化 + 至少一次语义(防丢失)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@KafkaListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;topics&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;OrderPaidEvent&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;groupId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;inventory-service&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;onMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OrderPaidEvent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 持久化到本地 outbox 表&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;outboxRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OutboxRecord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getEventId&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;PENDING&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 业务处理&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;inventoryService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;reserve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getOrderId&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getItems&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 处理完标记&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;outboxRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;markProcessed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getEventId&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 2. 幂等消费(防重复)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@Service&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;InventoryEventHandler&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OrderPaidEvent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 用 eventId 去重:同样的 eventId 处理过一次就跳过&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;processedRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getEventId&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;inventoryService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;reserve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getOrderId&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getItems&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;processedRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getEventId&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Instant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 3. 顺序保证(防错乱)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Kafka 的 partition key 选 orderId,同一个订单的所有事件进同一个 partition&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@Bean&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;NewTopic&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;orderPaidTopic&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TopicBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;OrderPaidEvent&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;partitions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;12&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;min.insync.replicas&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 生产时用 orderId 作为 key&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;kafkaTemplate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;OrderPaidEvent&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 4. 死信队列 + 人工兜底(防状态错位)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@KafkaListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;topics&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;OrderPaidEvent.DLQ&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;groupId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;dlq-handler&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;onDlq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OrderPaidEvent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;alertService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sendAlert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;事件处理失败,需人工处理&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;4 道防线的成本与收益&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;持久化:性能开销 5%,一致性提升 80%&lt;/li&gt;
&lt;li&gt;幂等:代码复杂度 +20%,重复消费问题清零&lt;/li&gt;
&lt;li&gt;顺序:Kafka 分区策略调优,顺序性提升到 99.99%&lt;/li&gt;
&lt;li&gt;死信队列:人工兜底,极端情况仍有出路&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;结果&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不一致工单:从月均 30 起 → 0 起&lt;/li&gt;
&lt;li&gt;大促后对账:T+1 自动对账,人工补单率 &amp;lt; 0.001%&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;教训&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;不要相信&amp;quot;消息中间件一定可靠&amp;quot;&lt;/strong&gt;——必须有应用层的 4 道防线&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;事件 schema 必须有版本号&lt;/strong&gt;——老消费者跑老 schema,新事件跑新 schema,演化兼容&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="45-案例-5异构系统融合--多语言微服务统一治理"&gt;4.5 案例 5:异构系统融合 — 多语言微服务统一治理
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;背景&lt;/strong&gt;:某跨国物流公司,核心系统有 4 种技术栈:Java(订单)、Go(调度)、Python(数据)、C#(遗留 ERP)。&amp;ldquo;微服务化&amp;quot;时,各团队按擅长选了不同语言,治理难度爆炸。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;痛点&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;4 种语言的注册中心不一致(Eureka / Consul / Nacos / 自研)&lt;/li&gt;
&lt;li&gt;4 种语言的配置中心不一致(Spring Cloud Config / Apollo / 自研)&lt;/li&gt;
&lt;li&gt;4 种语言的链路追踪不一致(Sleuth+Zipkin / OpenTelemetry+Jaeger / 自研)&lt;/li&gt;
&lt;li&gt;一次故障排错 4 个团队,沟通成本极高&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;解决方案:统一基础设施 + 各语言 SDK&lt;/strong&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;Java&lt;/th&gt;
					&lt;th&gt;Go&lt;/th&gt;
					&lt;th&gt;Python&lt;/th&gt;
					&lt;th&gt;C#&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;Nacos&lt;/td&gt;
					&lt;td&gt;Nacos-Spring&lt;/td&gt;
					&lt;td&gt;Nacos-Go&lt;/td&gt;
					&lt;td&gt;Nacos-Python&lt;/td&gt;
					&lt;td&gt;Nacos-C#&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;配置中心&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Nacos&lt;/td&gt;
					&lt;td&gt;Nacos-Spring&lt;/td&gt;
					&lt;td&gt;Nacos-Go&lt;/td&gt;
					&lt;td&gt;Nacos-Python&lt;/td&gt;
					&lt;td&gt;自研 HTTP 调用 Nacos OpenAPI&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;链路追踪&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Jaeger(OTel 标准)&lt;/td&gt;
					&lt;td&gt;OTel Java&lt;/td&gt;
					&lt;td&gt;OTel Go&lt;/td&gt;
					&lt;td&gt;OTel Python&lt;/td&gt;
					&lt;td&gt;OTel .NET&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;日志&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Loki(统一 JSON 格式)&lt;/td&gt;
					&lt;td&gt;Logback + JSON Encoder&lt;/td&gt;
					&lt;td&gt;zap + JSON&lt;/td&gt;
					&lt;td&gt;logging + JSON&lt;/td&gt;
					&lt;td&gt;log4net + JSON&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;指标&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Prometheus&lt;/td&gt;
					&lt;td&gt;Micrometer&lt;/td&gt;
					&lt;td&gt;prometheus/client_python&lt;/td&gt;
					&lt;td&gt;prometheus-net&lt;/td&gt;
					&lt;td&gt;Micrometer&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;CI/CD&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Jenkins + ArgoCD&lt;/td&gt;
					&lt;td&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;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;基础设施统一,但应用代码不强制统一&lt;/strong&gt;——Java 用 Spring Boot,Go 用 Gin,Python 用 FastAPI,各团队按擅长&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;统一标准 &amp;gt; 统一实现&lt;/strong&gt;——所有语言都用 OpenTelemetry 协议(只要符合协议,实现自选)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;抽象出&amp;quot;语言无关&amp;quot;的 API 网关 + Service Mesh&lt;/strong&gt;——所有外部调用走网关,所有内部调用走 Mesh(Istio/Linkerd)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;结果(12 个月后)&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;4 种语言,1 套治理(只要学会 OTel + Prometheus + Loki,就能排查所有服务)&lt;/li&gt;
&lt;li&gt;服务发现延迟统一 &amp;lt; 50ms&lt;/li&gt;
&lt;li&gt;跨语言链路追踪:从&amp;quot;4 个工具 + 4 个 UI&amp;rdquo; → &amp;ldquo;1 个 Jaeger UI 全部串起来&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;教训&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;不要因为&amp;quot;统一&amp;quot;而强制&amp;quot;同一技术栈&amp;quot;&lt;/strong&gt;——业务复杂、多团队异构是常态&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;统一治理的&amp;quot;协议层&amp;quot;是 OpenTelemetry&lt;/strong&gt;——它跨语言、跨框架、开源标准,5 年内不会被淘汰&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;异构系统最怕&amp;quot;每个团队自己造轮子&amp;quot;&lt;/strong&gt;——基础设施部门必须提供&amp;quot;开箱即用&amp;quot;的 SDK&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="46-案例-6遗留系统拆解--绞杀者模式--数据双写"&gt;4.6 案例 6:遗留系统拆解 — 绞杀者模式 + 数据双写
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;背景&lt;/strong&gt;:某传统国企,核心 ERP 是 2008 年的 C# .NET Framework 2.0,代码 120 万行,改任何功能都要发布 .NET 包 + 重启 IIS 集群。新业务(移动端、API)想用,但这个老系统&lt;strong&gt;完全不能扩展&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;痛点&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;老系统 18 年没人敢动,改一行代码要全量回归(测试 5 个团队、2 周)&lt;/li&gt;
&lt;li&gt;移动端想用老系统的&amp;quot;订单查询&amp;quot;功能,但老系统只暴露 COM 组件,跨进程调用慢且不稳定&lt;/li&gt;
&lt;li&gt;2018 年后微软停止 .NET Framework 2.0 支持,安全漏洞没人修&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;但完全替换 ERP 不可能&lt;/strong&gt;——20 年业务规则沉淀在代码里,没人能说清楚&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;解决方案:绞杀者模式(Strangler Fig)&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;阶段 1(0-3 个月):基础设施
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; - 老 ERP 前面架 API Gateway
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; - 新建 new-erp-service(Java Spring Boot),先实现&amp;#34;订单查询&amp;#34;一个新功能
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; - API Gateway 路由:移动端 → new-erp(新),其他 → 老 ERP
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;阶段 2(3-12 个月):逐步迁移
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; - 每月迁移 1-2 个功能(订单查询 → 订单创建 → 订单退款 ...)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; - 每迁移一个功能,API Gateway 切换路由
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; - 老 ERP 代码逐月减少,新服务逐月增加
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;阶段 3(12-18 个月):数据迁移
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; - 老的&amp;#34;t_order&amp;#34;表数据全量同步到 new-erp-service 的新表
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; - 双写期 3 个月:同时写老表 + 新表
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; - 读用开关切换
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; - 3 个月后废弃老表
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;阶段 4(18-24 个月):老 ERP 下线
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; - 路由全部切换到 new-erp
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; - 老 .NET 进程灰度下线
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; - 服务器资源回收
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;关键技术点&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;API Gateway 是&amp;quot;切换开关&amp;quot;&lt;/strong&gt;——所有路由都集中管理,切换可秒级&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据双写期必须&amp;quot;可观测&amp;quot;&lt;/strong&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;li&gt;&lt;strong&gt;老 ERP 的&amp;quot;业务规则&amp;quot;必须文档化&lt;/strong&gt;——迁移前 2 个月专门做&amp;quot;业务规则考古&amp;quot;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;结果(24 个月后)&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;老的 .NET 系统完全下线,释放 80% 服务器资源&lt;/li&gt;
&lt;li&gt;新系统(.NET Core 6 + Spring Boot 混部)支持移动端 + Web + 开放 API&lt;/li&gt;
&lt;li&gt;改一个订单功能:从 2 周 → 2 小时&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;核心业务规则零损失&lt;/strong&gt;——通过 24 个月的&amp;quot;双写 + 对账&amp;quot;保障&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;教训&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;绞杀者模式是&amp;quot;渐进式重构&amp;quot;的标准答案&lt;/strong&gt;——不要做&amp;quot;big bang&amp;quot;(一次性重写,风险极高)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据迁移的&amp;quot;双写期&amp;quot;是必须经历的风险&lt;/strong&gt;——不要为了&amp;quot;快&amp;quot;跳过&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;老系统的&amp;quot;业务规则&amp;quot;是最值钱的资产&lt;/strong&gt;——架构可能过时,业务规则 20 年不变&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="五踩坑清单拆分失败的-10-个真实教训"&gt;五、踩坑清单:拆分失败的 10 个真实教训
&lt;/h2&gt;
 &lt;blockquote&gt;
 &lt;p&gt;这一节不讲方法论,讲 10 个我亲眼见过(或亲身踩过)的&amp;quot;拆分失败&amp;quot;教训。每条都附&amp;quot;症状 + 根因 + 解决&amp;quot;。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;1. 没做事件风暴就拆服务&lt;/strong&gt;——症状:微服务拆完后业务逻辑散落,跨服务调用乱成一团;根因:没识别清楚限界上下文;解决:拆之前先 2 天事件风暴。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. 拆得&amp;quot;过细&amp;quot;&lt;/strong&gt;——症状:一个聚合根一个服务,本地事务变分布式事务,性能差 + 一致性差;根因:把&amp;quot;DDD 实体&amp;quot;当&amp;quot;微服务边界&amp;quot;;解决:一组紧密相关的聚合根 = 一个服务。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. 共享数据库&lt;/strong&gt;——症状:DBA 改一个字段,多个服务挂;根因:微服务&amp;quot;逻辑上拆了,物理上没拆&amp;quot;;解决:每个服务独立 Schema,跨服务走 API/事件。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. 同步链路太多&lt;/strong&gt;——症状:用户改昵称要等 5 个服务响应,3 秒后才返回;根因:用了同步 RPC 而不是异步事件;解决:同步只用于&amp;quot;读自己的数据&amp;quot;,其他用事件。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5. 没有可观测就拆&lt;/strong&gt;——症状:出问题找不到根因,排错 3 天;根因:链路追踪 + 统一日志没建;解决:先建 SkyWalking + ELK + Prometheus,再建微服务。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6. 团队结构没动&lt;/strong&gt;——症状:微服务是 5 个团队协作,部署时还是 2 周排期;根因:康威定律反着干;解决:组织先行,按业务域组建 Stream-aligned 团队。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7. 分布式事务滥用&lt;/strong&gt;——症状:Seata AT 模式全链路,一个服务挂全链路卡 30 秒;根因:把&amp;quot;必须强一致&amp;quot;扩展到所有场景;解决:优先事件 + 最终一致,只在金融核心用 Seata。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8. 事件 schema 没版本号&lt;/strong&gt;——症状:消费方升级后,老事件处理报错;根因:事件结构变了但没兼容老版本;解决:每个事件带 &lt;code&gt;schemaVersion&lt;/code&gt; 字段,消费者按版本路由。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9. 没有 API 版本管理&lt;/strong&gt;——症状:改一个支付接口,订单服务 27 个调用方全挂;根因:接口改动没 RFC 流程;解决:OpenAPI 3.0 + RFC 评审 + 老版本保留 6 个月。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10. 老接口没及时下线&lt;/strong&gt;——症状:系统里同时跑 V1/V2/V3 接口,代码越积越多;根因:没设废弃时间表;解决:每个老接口设 deprecationDate,到期强制下线。&lt;/p&gt;
&lt;p&gt;这 10 条没有一条是&amp;quot;技术难题&amp;quot;,都是&amp;quot;流程 + 纪律&amp;quot;问题。&lt;strong&gt;微服务拆分的成功,80% 靠组织治理,20% 靠技术&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id="结语把拆换成治"&gt;结语:把&amp;quot;拆&amp;quot;换成&amp;quot;治&amp;quot;
&lt;/h2&gt;&lt;p&gt;回到开头 CEO 的那个问题:&amp;ldquo;已经是微服务了,为什么改个东西比单体还慢?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;答案是:&lt;strong&gt;没有&amp;quot;自治&amp;quot;的拆分,只是把大泥球切成了小泥球&lt;/strong&gt;。真正的微服务,不是&amp;quot;部署了 N 个 Spring Boot 应用&amp;quot;,而是:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;业务边界清晰&lt;/strong&gt;(DDD 限界上下文)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;团队边界对齐&lt;/strong&gt;(Team Topologies)&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;如果只学一招,我建议把&amp;quot;康威定律&amp;quot;贴在显示器上——&lt;strong&gt;任何架构决策前,先看组织结构;组织没动,先动组织&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;这就是我那次从 38 个微服务大泥球到 12 个真正自治服务的全部心法。后续的 Nacos 怎么选(第 6 篇)、网关怎么限流(第 7 篇)、熔断怎么做(第 8 篇),都是这套心法的&amp;quot;组件落地&amp;quot;。&lt;strong&gt;先有边界,再有服务;先有组织,再有架构&lt;/strong&gt;——这是 10 年 Java 微服务实践最值钱的 5 个字。&lt;/p&gt;</description></item></channel></rss>