
去年我们接到一个挺棘手的任务重构一套每天高峰要扛几十万级并发请求、但代码还停留在事务脚本阶段的闪购系统。当时我对DDD能不能在这种老系统上落地心里是没底的——不是概念难而是老系统的业务逻辑早散得到处都是靠人工梳理工期根本排不开。后来我们换了个思路把AI当成“建模速记员”和“代码生成工人”把团队里的架构师和领域专家拉回到建模本身。重构结束后的结果就是标题里那两个数字核心链路代码量减少52%单需求平均交付成本下降超过75%。这篇把整套打法完整拆开从为什么选DDD、AI在建模环节怎么用到代码生成和量化口径最后是我们踩过的坑尽量讲透。需要先说明的是为避免不必要的品牌指向下文所有业务描述都以“某电商平台的闪购业务系统”为背景。这套方法的适用面远不止电商凡是存在大量业务规则、多人协作、长期演进的中后台系统思路都可以平移。1. 为什么说老系统改不动才轮到DDD出手1.1 一个典型闪购模块的事务脚本长什么样几乎所有“改不动”的系统病灶都很相似。拿闪购最常见的下单场景来说传统代码往往是一个巨大的下单Service里面依次塞了几十个步骤校验活动、校验用户资格、查询库存、扣减库存、计算优惠、校验优惠、创建订单、发送消息……每一步都直接操作数据库中间夹杂着一堆if else。业务上明明有“闪购活动”“库存”“订单”“优惠券”这些清晰概念代码里却被压扁成一张“流水线表”。更麻烦的是订单状态直接散落在多个Service里支付回调改一下订单状态超时任务也改一下订单状态活动减库存和售后加库存也各写各的。久而久之“订单状态机”成了薛定谔的猫——谁都在摸谁都不知道全貌。这种代码其实有个专门的名字贫血模型加事务脚本。它最大的问题不是行数多而是“领域逻辑”和“技术实现”完全搅在一起。改一个优惠规则可能要联动下单、支付、对账三个服务加一个新活动类型几乎要把整条链路重新走一遍。1.2 没有领域边界的代价改一个需求要多花多少成本我们重构前做过一次粗略统计过去一年团队60%的迭代都集中在下单与促销模块而这两个模块的代码耦合度最高。一个典型的“延长闪购活动时间”需求放在老系统里要动7个接口、20多处状态判断联调时谁都不敢保证没漏。这里最耗时的不是写代码而是开会。业务同学说“活动快照”开发理解成“把活动配置复制一份”业务说“超时取消”开发理解成“把订单状态改成已取消”。术语不一致代码里的命名跟着漂移——同一个概念在不同服务里有三个名字。这就是典型的缺“统一语言”Ubiquitous Language。DDD的核心理念说白了就两件事第一先把业务的边界划清楚每个领域模型只关心自己的事第二用统一的业务语言沟通代码命名跟着业务术语走而不是跟着数据库表名走。1.3 AI在重构里的真实定位不是替代架构师而是放大产能很多文章把“AI重构”写得像魔法丢一个旧项目进去AI自动吐出漂亮整洁的新代码。实际完全不是这样。我们尝试过让AI直接“翻译”老代码结果它把事务脚本里的坏味道原封不动搬到了新代码里——因为老代码本身就是一种不太合理的表达AI照着学只能学到错误范式。真正的分工应该是人负责“知道业务到底想要什么”AI负责“把已经想清楚的业务翻译成高质量代码”。说得再直白一点DDD的建模过程特别适合AI参与因为建模需要大量枚举事件、整理词汇、拆分工序这些是LLM的强项而最终的边界划分、聚合设计、业务规则取舍仍然要人来拍板。我们团队当时有个判断如果硬靠人工做事件风暴和代码迁移至少得8周AI只能帮我们压缩工期不能帮我们做决策。实际干下来这个判断是对的。AI把我们从大量机械劳动里解放出来让我们有更多时间讨论真正的业务问题。2. 事件风暴先行AI怎样把业务规则变成统一语言2.1 事件风暴不是“开会”而是一条事件时间轴大多数团队对事件风暴的理解容易跑偏觉得就是邀请业务专家和技术人员坐在一起贴便签纸。其实事件风暴的核心产出物是一条按时间顺序排列的事件流。比如闪购下单链路我们梳理出这样一串核心事件用户提交下单请求系统校验闪购活动资格系统锁定活动库存系统计算并应用优惠订单状态变为已提交支付结果通知到达订单状态变为已支付用户未支付系统触发超时取消库存回补每个事件都对应一个明确的业务结果。这么做最大的价值是把“业务发生了什么”和“系统要做什么”分开了。事件说出来业务专家听得懂落到代码里事件就是领域事件消息结构也自然清晰。2.2 用AI做流程文本清洗和事件提取我们手头有一大堆老接口文档、历史需求单、会议纪要全是口语化的描述。比如“用户下单的时候如果这个券已经过期了那就不能用还要给提示”。这些话句式混乱、术语不统一靠人读非常低效。我的做法是把这些原始文本整理后扔给AI让它先做“事件提取”提示词大致长这样下面是一段闪购下单流程的业务描述请提取出 1. 所有业务事件用“xxx已完成/已拒绝”的过去时命名 2. 触发这些事件的命令或动作 3. 每个事件发生时需要校验的业务规则 4. 涉及的实体名词 要求统一名词不要出现“订单状态改为”这类技术表述改用业务事件描述例如“订单已提交”。原始描述如下 ……这一步产出的候选事件可能有一百多个有一定重复和噪音但没关系。AI负责把覆盖面铺开人工负责收敛和纠正。实测下来AI提取出的候选事件覆盖了老系统80%以上的行为剩下20%藏在代码逻辑里或者只在业务口头流传必须靠领域专家补充。2.3 提示词里最重要的三个约束统一语言、事件命名规范、命令与事件的区分用AI做事件提取提示词里的约束比问题本身更重要。我们迭代了很多轮最终沉淀出三个关键约束第一强制统一语言。业务同学可能一会儿说“过期”、一会儿说“失效”但代码里只能选一个词。我们在提示词里明确“以下每个概念只能有一个标准名称请先给出一份术语对照表”。第二强制事件采用过去时命名。因为事件代表已经发生的事实用“订单已取消”而不是“取消订单”。这个约束能让后续领域事件的建模一致得多。第三强制区分“命令”和“事件”。命令是外部请求比如“用户提交订单”事件是系统内部产生的已发生事实比如“订单已提交”。AI如果不区分很容易把命令和事件混在一起导致领域模型语义混乱。这里分享一个我们踩过的小坑有一次为了省事没在提示词里约定事件命名AI生成了“订单超时取消”“订单支付成功”这样的中英混杂命名看起来没问题但后面做事件溯源时发现消息主题名称和事件类名对不上排查了很久。统一命名规范这件事越早定越好。2.4 建模产物如何反向校验业务用“如果”测试逼问边界事件风暴产出事件轴以后不能直接进编码还要做一轮边界校验。我习惯用“如果……会怎样”来逼问模型如果用户下单时活动库存只剩1件但用户同时下了2件系统怎么处理如果用户支付时优惠券已经过期事件顺序应该是“优惠失效”还是“支付失败”如果支付回调延迟了2小时超时取消任务和支付回调谁先到这些场景AI是没法代替业务判断的但可以用AI帮忙列候选场景。做法是把已经梳理好的事件轴和聚合边界发给AI让它扮演“毒舌测试员”列出所有可能的边界场景清单再让业务专家逐个确认。这一步看起来不起眼却直接决定聚合设计是否可靠。因为聚合的本质就是“保证不变量”的边界——比如库存聚合要保证“不能超卖”优惠聚合要保证“一张券同一个订单只能用一次”。边界没划清楚后面生成代码越快返工越多。3. 让AI生成领域层代码提示词、脚手架与三层验证3.1 从事件轴到聚合设计把“锁库存”变成领域服务事件轴定完之后下一步是把事件归类到聚合。例如“库存锁定”“库存回补”这些事件加上“查询剩余库存”“预占库存”这些命令显然应该聚到同一个库存聚合里优惠相关的事件聚到优惠券聚合订单状态变迁聚到订单聚合。聚合的粒度不能太小否则又退回事务脚本也不能太大否则聚合内并发冲突严重。我们的经验是一次购物车结算涉及到的库存预占、优惠计算、订单生成分别拆成三个聚合通过领域事件解耦而不是把三个聚合塞进一个“大结算根”。这一步设计好了AI生成代码的“输入”就齐了。我们把聚合清单、事件清单、命令清单、业务规则清单打包成一份结构化的“建模契约”作为后续所有生成提示词的锚点。3.2 提示词工程模板让AI输出DDD分层代码以下是我们实际使用过、效果还不错的生成提示词模板核心思路是“先给上下文再给任务最后给约束”。你可以直接复制改造你是一位资深DDD技术专家。下面是一个“库存聚合”的建模契约 聚合名称库存 核心实体sku库存 不变量闪购库存不允许超卖 领域命令 - 预占库存请求携带skuId、数量、活动Id - 释放预占请求携带预占单号 领域事件 - 库存已预占事件携带预占单号、skuId、数量 - 库存已释放事件携带预占单号、skuId、数量 仓储接口 - 按skuId查找可用库存 - 保存预占记录 请按端口-适配器架构生成Java代码要求 1. 领域层不依赖Spring、不依赖数据库 2. 聚合内部的业务规则写在领域服务中而不是写在应用服务中 3. 外部输入通过命令对象传入所有校验失败抛出明确业务异常 4. 返回结果使用领域事件返回值不要直接在聚合里发消息。根据这个模板生成出的聚合根代码结构基本符合预期。下面是一段浓缩后的示意去掉了大量细节public class SkuStock { private final SkuId skuId; private final Quantity available; private final ListPreemption preemptions; public PreemptResult preempt(PreemptCommand command) { if (available.isLessThan(command.getQuantity())) { throw new BusinessException(闪购库存不足); } Preemption p Preemption.create(command.getPreemptionNo(), skuId, command.getQuantity()); preemptions.add(p); return PreemptResult.of(StockPreempted.of(p)); } public ReleaseResult release(ReleaseCommand command) { Preemption p findByNo(command.getPreemptionNo()); p.release(); return ReleaseResult.of(StockReleased.of(p)); } }这种代码最大的好处是领域规则集中在聚合根里“不允许超卖”这条业务规则你一眼能看到而不是藏在某个Service第80行的if判断里。3.3 三层验证机制编译通过、单元测试、契约测试AI生成的代码一定不能直接合入主干。我们的做法是三道闸第一层是编译和静态检查。生成完代码后让AI自己检查一遍依赖和语法然后跑构建跑不过就带着报错信息“回喂”给AI修改。这一层能过滤掉90%的低级错误。第二层是领域级单元测试。因为我们要求领域层不依赖Spring所以单元测试很容易写。让AI根据生成的聚合代码同时生成对应的测试用例覆盖正常路径和异常路径。异常路径特别重要比如“库存不足时预占应抛出异常且不产生预占记录”。第三层是契约测试。我们用一条条领域事件作为契约验证“执行命令后是否产生了预期的事件序列”。这一步能抓住很多AI的“自以为是”比如它可能在聚合里直接调了数据库违反了纯领域规则或者抛异常时把业务规则暴露成了NullPointerException。三层验证跑完代码才真正进入人工代码评审流程。3.4 增量生成策略一次只生成一个用例别让AI一口气写完我们在早期犯过一个严重错误希望AI一次性生成整个订单模块约好了所有聚合、所有流程结果AI生成了几千行代码结构没错但细节一塌糊涂命名不一致、校验规则漏了、事件顺序错乱。改起来比手写还痛苦。后来我们把大任务拆成“用例级”的增量生成。一次只聚焦一个业务场景比如“用户提交订单并成功预占库存”生成对应的应用服务、领域服务、领域事件和测试验证通过后再生成“支付成功”“超时取消”等场景。这样做还有一个额外的好处模块与模块之间天然形成“已交付”的边界代码评审负担小回归风险也小。AI适合当“车间工人”按工单一个个生产谁让AI直接当“总包”交付一栋楼谁就得自食苦果。4. 52%和75%是怎么算出来的量化口径与真实占比4.1 代码量基线不是总行数而是“有效变更行数”很多人看到“代码量减少52%”会本能地问是不是把老代码里的注释、空行、自动生成代码都剔掉了我们统计的时候确实做了区分而且我认为这才是对的口径。我们对比的不是git总行数而是“业务逻辑的有效变更行数”。具体算法是先按模块拆分剔除空行、注释、第三方模板代码、数据库脚本和构建文件再统计领域层、应用层、基础设施层里需要人工维护的业务代码。下表是重构前后的部分模块对比数据经过脱敏处理但比例是真实的模块重构前有效代码行数重构后有效代码行数下降比例下单流程81003900-52%优惠计算54002300-57%库存预占与回补47002600-45%订单状态流转63003100-51%合计2450011900-51.4%代码量减少的核心来源有三个一是多个Service里重复的状态流转逻辑被收敛到订单聚合二是原来散落的优惠判断规则被统一到优惠聚合三是大量模板式CRUD代码被通用仓储和事件发布机制替代。还有一点容易被忽略新代码中“领域规则”的密度反而变高了。同样的代码量含金量完全不一样。这也是我后来坚持向大家强调的——不要只盯着行数要看代码里“死逻辑”和“活逻辑”的比例。死的CRUD少一半活的业务规则更集中这才是重构最大的收获。4.2 成本降低75%的统计口径不是“人力投入”而是“业务交付成本”“成本降低75%”听起来很夸张但它不是指程序员总工资少了75%而是指“单需求平均交付成本”。我们以过去一个季度为观察窗口统计里包括沟通、设计、开发、联调、测试、上线、返工的全部人天。重构前一个中等复杂度的闪购需求比如“新增一种限购规则”平均要14个工作日重构后同样类型需求平均只要3个工作日降幅接近79%。平均下来按75%计是有依据的。这背后有两层效率来源第一层来自DDD边界清楚了联调变成“填充接口之间的缝隙”而不是“互相猜对方逻辑”第二层来自AI机械编码量大幅减少开发把时间花在写契约、评审、测试上。4.3 这些数字有多少来自AI多少来自DDD我特别想给这个量化结论泼一点冷水。不少团队看到“AIDDD”就觉得是AI的功劳我们内部复盘后的结论是五五开。DDD带来的收益是系统性的边界清晰、命名统一、领域规则收敛这些和AI没有直接关系是人脑建模的结果。AI带来的收益是执行侧同样的建模成果传统编码要28个工作日AI辅助后大约10个工作日就能完成省下的是纯粹的机械劳动时间。所以如果要复刻这个方案千万别想着“买了AI工具就能自动重构”。AI省下的只是“从设计图到代码”的距离而真正决定重构成败的是设计图本身的质量。5. AI生成的“完美代码”为什么不能直接上生产我们踩过的坑5.1 提示词写得太细AI产出反而更僵化我们一开始追求“提示词越细越好”把所有类的命名、所有方法的签名、所有目录结构都写清楚结果AI输出的代码像“用模板硬套”出现了大量重复且无意义的中间方法甚至出现了同一种校验逻辑在三个类里各写一份的情况。代码评审时被同事指着鼻子问这真的是AI写的看着比人写的还烂。后来我们调整了策略提示词里只约束“边界、规则、不变量、依赖方向”不约束“方法内部怎么实现”。给AI留出表达空间它反而能生成更干净的代码。这就像你给设计师定“风格、配色、预算”但别指挥人家“这里加个按钮、那里加个像素”。5.2 领域事件被AI“优化”得越来越像旧的事务脚本我们让AI按“命令-领域方法-领域事件”的模式生成代码后它经常把领域事件直接转成对数据库的更新操作比如在应用服务里写“先扣库存、再算优惠、再创建订单”把事件当成注释而不是一等公民。这是LLM被老代码风格“污染”的结果。我们的解法是在代码评审中增加一条硬性规定应用服务只做两件事——把命令转给领域层然后发布领域事件。谁要是把业务规则写在应用层谁就重写。规则简单粗暴但管用。5.3 过度抽象AI自动创造了三个泛型基类有一次AI为了让代码“更优雅”自动造了一套抽象层体系BaseAggregate、BaseService、BaseRepository三层泛型嵌套看起来很高大上实际用起来要打开五六个文件才能理解一个最简单的查询。这里我要说一句可能不讨喜的话过度抽象比重复代码更可怕。重复代码至少每个地方都能独立改抽象一旦失控改一个细节就牵一发动全身。我们对AI生成的代码专门加了一条约束禁止为了减少重复而引入新的抽象层如果真的出现明显重复先合并但合并前要有人工评审确认。5.4 代码注释里藏着幻觉生成代码与实际逻辑不一致AI生成的注释特别有迷惑性。它可能写“如果用户支付超时则取消订单并释放库存”但实际代码只在部分分支做了释放注释属于“脑补合理场景”。这个问题比代码错误更难发现因为注释会骗你让你跳过真实逻辑。我们后来专门做了一轮“注释与代码一致性检查”把AI生成的注释提取出来逐条对代码核对。这轮检查帮我们抓出了十多个潜在线上故障。5.5 回归测试覆盖要人工盯AI改代码时经常把边界情况写丢AI的“上下文记忆”其实比人还差。它帮你修了一个库存扣减的Bug可能顺手把“数量为0时校验失败”的分支删掉了第三天你回过头来才发现不对劲。我们的对策是所有AI修改过的代码必须连带更新对应的单测用例而且测试用例要覆盖到边界值。没有测试用例守护的AI重构等于在悬崖边开车。这条规则看起来是常识但在AI生成的节奏下特别容易被忽略——因为它修改得太快了快到人类评审根本来不及反应。6. 什么样的团队和系统适合复刻这套打法6.1 团队画像领域专家、架构师、程序员三者的分工如何调整AI辅助DDD重构对团队的要求不是变低了而是变了。传统DDD需要领域专家、架构师、程序员三者密切配合引入AI后架构师的工作量更重了——他要把建模契约写得足够精确才能让AI稳定输出程序员的工作量从“写代码”变成了“验证代码、评审代码、修边界”领域专家依然要参与事件风暴但要更早、更频繁地做决策。如果我们团队缺少任何一个角色这套打法都会出问题。没有领域专家建模契约就是空壳AI生成的代码再规范也没用没有架构师契约里全是漏洞没有会写提示词和做评审的程序员AI生成的就是一堆需要返工的半成品。6.2 系统画像什么样的老系统适合“AIDDD”重构这套打法不是万能的。根据我们自己的观察适合重构的老系统通常有三个特征业务稳定核心流程长期不会大改规则复杂但明确可以被梳理成清晰的事件轴历史包袱重但新旧系统可以逐步替换。反过来如果业务本身还在剧烈变化期比如每两周就要新增一个完全没想清楚的活动玩法那DDD反而会拖慢节奏因为建模赶不上变化。这种场景下优先用轻量流程先把业务跑通再考虑领域建模。6.3 三个必要条件事件风暴记录、自动化测试基线、统一的代码生成脚手架想复刻至少要提前准备好三样东西第一完整的事件风暴记录。这是AI生成代码时最重要的业务上下文。文档要结构化按聚合、事件、命令、规则分别整理而不是把会议纪要丢给AI。第二自动化测试基线。重构之前的旧系统最好有一套覆盖面足够广的回归测试否则新代码跑得再快你也不敢上线。我们重构时先花了一周时间给核心链路补了冒烟测试这一步帮我们兜住了大量重构引入的回归。第三统一的代码生成脚手架。AI生成代码时需要一个固定的目标结构包括目录分层、依赖规则、事件总线、仓储框架。脚手架不统一AI生成的代码风格就会飘评审成本骤增。6.4 不建议复刻的场景规则频繁变动且领域专家严重缺位的系统最后说一个容易被忽略的反例。我们在另一个项目里尝试过同样的方案那个系统是后台配置管理类系统业务规则几乎隔几天就变而且没有专职的领域专家全靠开发自己猜业务意图。结果AI生成的建模契约很快过时代码从“领域清晰”退化成“多了一层壳的CRUD”收益远低于预期。那段经历让我更坚定一个看法AIDDD的杠杆只有在“建模质量足够高”的时候才是正的。建模质量不够AI只会放大混乱不会制造秩序。所以如果你所在团队连一次认真的业务对齐会议都凑不齐人建议先别急着上AI先把人和业务对齐了再说。闪购系统重构完到现在已经跑了一个大促周期最直观的感受是以前大促前要开好几次紧急会议梳理链路这次基本是照着事件轴线对一遍就行。AI和DDD的组合给我的启发是工具再强也只是把人的判断打磨得更锋利而判断本身——业务边界在哪、哪些规则不能妥协、哪些职责必须回收——永远是团队自己做的功课。