多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

AI友好型架构下的DDD:用限界上下文与领域事件驯服LLM

AI友好型架构下的DDD:用限界上下文与领域事件驯服LLM 去年年底我带的一个AI供应链项目上线后问题频出模型经常调用错误接口、上下文窗口放不下完整业务规则、生成的编排逻辑悄悄绕过了库存校验。团队的结论是AI太笨可我把代码翻了一遍就发现问题根本不在模型而在于我们没有任何领域边界——所有业务逻辑都拍平在一个巨大的提示词和一堆裸接口里。那一刻我意识到AI真正改变的不是要不要建模而是怎么建模。这篇文章就围绕AI友好型架构这个主题聊一个绕不开的问题AI时代我们还需要DDD领域驱动设计吗我会用实际项目的经验和踩坑记录把限界上下文聚合领域事件这些DDD概念重新放回AI工程实践里看看哪些还能打哪些必须进化以及一套可以直接落地的改造方案。适合正在做AI Agent接入业务系统的人、被AI打乱建模节奏的团队以及想给AI项目搭一套靠谱架构的技术管理者。1. 争议的起点为什么大家开始觉得DDD过时了大概从ChatGPT能写代码那天起业界就流行一个说法DDD可以进坟墓了。理由听起来很合理——模型看一眼表结构就能生成CRUD写个实体、仓储、应用服务也不在话下既然AI把这些活全干了我们干嘛还要费劲先画一堆聚合根和限界上下文1.1 被LLM绕过的领域建模这个观点我太熟了。我见过不止一个团队新项目启动时直接跳过DDD的战略设计和战术设计把建表SQL往AI工具里一贴让模型输出Mapper和Service。说实话在增删改查层面这条路确实跑得通。AI生成的数据访问代码甚至比人手写的更规范——命名统一、分页处理齐全、还自带单元测试。于是很多人的结论顺理成章领域模型阶段省掉吧直接表驱动提示词驱动。但问题在于它只在系统早期有效。业务逻辑一旦复杂起来你会发现那些用AI快速生成的代码里业务规则是散落在Service各处的。今天模型帮你加了个订单状态判断明天你又提示它补了个库存校验后天业务方说要改规则你都不知道该去改哪一行——因为根本没有一个地方集中表达订单这个概念。1.2 DDD过时论的三个论据与我的质疑我把唱衰DDD的声音归纳了一下大概有三类下面用表格逐条拆解论据常见说法我的回应LLM不需要UML也能写业务代码AI直接读接口文档就能生成实现建模环节纯属浪费时间AI生成的是转换逻辑不是业务决策逻辑。它不知道你这笔订单为什么必须走审批流这个知识你如果不建模就只会藏在提示词里Agent用自然语言直接编排业务以后业务规则用自然语言写在Prompt里模型自己懂不需要领域层Prompt里的规则无法被测试、无法被复用、无法被静态检查。自然语言是给模型看的模型会犯幻觉错误所以它更需要确定性的边界兜底DDD太重AI迭代太快跟不上事件风暴开两三天AI一周就把功能做完了DDD拖后腿重的从来不是DDD本身是过度设计。AI时代的DDD可以裁剪成轻量边界只保留限界上下文、领域事件和开放接口不搞复杂映射和抽象层级我并不是在说DDD的每一个组件都能原样照搬。事实上传统DDD里那一堆抽象基类、领域服务、防腐层在AI时代确实会显得笨重。但注意DDD的核心不是代码模板而是业务知识的显式化。这个核心不仅没有过时反而在AI时代变得更加关键——因为你得把业务知识喂给一个会一本正经胡说八道的模型不给它清晰的围栏它就敢给你编出各种不存在的状态和规则。2. AI友好型架构到底在解决什么问题在讨论DDD能不能用之前得先搞清楚一件事什么叫AI友好型架构。这个词听起来玄乎其实落到工程上就是指——让LLM或Agent能在你的系统里稳定、可控、可预测地完成任务的那种架构。它不是给AI住的豪华宫殿而是给AI划的跑马场。2.1 AI项目的失败模式往往不在领域逻辑我做过的AI项目里真正翻车的场景几乎都不是模型本身笨而是架构不友好。最常见的几种失败模式上下文爆炸为了让AI理解业务流程把几十条业务规则全部塞进Prompt结果几轮对话下来上下文窗口塞满模型开始忘事、答非所问。工具错选一个Agent挂了50个工具描述写得含糊结果模型总是调错接口——比如把查订单明细调成查商品库存返回的数据对不上。幻觉性业务操作模型自己编造了一个已完成出库的状态企图跳过校验逻辑直接触达数据层。这个现象在只给模型开放数据库读写权限时不罕见。注意这些失败有一个共同点它们不是业务规则写错了而是模型访问业务规则的方式错了。领域逻辑放在代码里是对的但你把它毫无结构地暴露给模型模型不知道边界在哪也不知道哪些能碰、哪些不能碰。2.2 AI友好型架构的四个关注点我踩过这些坑之后慢慢总结出AI友好型架构至少要满足四个条件稳定的接口契约AI通过工具或API访问系统时每一次调用的入参和出参结构必须绝对稳定。模型最怕这次返回这个字段、下次返回那个字段的不确定性。明确的职责边界一个Agent只负责一个业务域。你说不清它的边界模型就更说不清。可裁剪的上下文输入给模型的上下文不是越多越好而是要精确。把与当前任务无关的领域规则全部剪掉只留它此刻需要的那一小组知识。完整的可观测性模型调了什么工具、走了什么流程、返回了什么结果全部要能追踪。没有可观测性AI系统出了问题你根本无从排查。发现没有这四个关注点恰恰和DDD的那套思想是天然对应的契约对应防腐层和聚合职责对应限界上下文上下文裁剪对应子域划分可观测性对应领域事件追踪。所以与其说AI时代要抛弃DDD不如说AI时代需要把DDD重新打磨成适合AI的形状。3. 当LLM进入业务系统DDD的哪些部分还能打既然AI友好型架构需要边界、契约和裁剪能力那DDD里那些经典概念对号入座之后你会发现它们不但没衰减反而重新有了用武之地。我挑三个最核心的来讲。3.1 限界上下文成为Agent的工作边界这是我觉得最有价值的一点。限界上下文用在AI时代直接就是Agent的职责切分依据。我做过一个电商售后系统最开始只搞了一个大Agent塞给它订单、支付、库存、物流全部知识。结果它处理退款时一会儿去找库存接口扣减一会儿又直接改订单状态乱得一塌糊涂。后来我按DDD的限界上下文把系统拆成三个Agent订单域Agent、库存域Agent、支付域Agent。每个Agent能调用的工具严格限定在自己的上下文范围内跨域操作必须走领域事件异步通知。这一改效果立竿见影。Agent不再跨域乱闯工具误调用率降了至少一半。背后的道理也简单模型和人一样范围越小、规则越明确、越不容易出错。限界上下文在传统DDD里是用来划分团队和模型的边界在AI时代它就是用来划分这个Agent能做什么、不能做什么的围栏。3.2 领域事件与工具函数的匹配DDD里的领域事件原本是用来做事件风暴、识别业务状态流转的。到了AI时代它们有一个更实用的用途直接转化为Agent编排流程的触发点和状态信号。举个例子在售后系统里我梳理出这样一组领域事件退款申请已提交、退款审批通过、库存已回补、订单状态已更新。这些事件原本是DDD建模时的分析产物现在它们变成了Agent的工作流节点Agent收到退款审批通过事件才知道下一步该调用库存回补工具收到库存回补完成事件才能更新订单状态如果库存回补超时未到Agent要上报异常而不是自己去操作库存。这就是一个非常典型的AI友好型设计不要让Agent理解整个领域的因果链只需要给它每一步需要响应的事件以及每个事件对应的动作接口。这种模式比把所有规则塞进Prompt要可靠得多因为事件本身就是确定的代码节点模型只是在事件之间做有限选择。3.3 聚合根与数据契约以订单为例有人可能问聚合根这套战术建模还需要吗我的答案是需要但要重新定位——聚合根最值钱的不是它的代码结构而是它定义了一个稳定不变的数据契约。拿订单举例。传统DDD里我们画一个订单聚合里面装订单项、收货地址、支付记录然后规定所有对订单的修改都必须经过订单聚合根。AI时代这个聚合根的对外形态就变成了Agent能看到的返回结构见下面这段简化代码// 领域模型订单聚合根简写 public class Order { private OrderId id; private OrderStatus status; private Money totalAmount; private ListOrderItem items; private ListDomainEvent events; public void approve() { ... } public void complete() { ... } // 对外暴露给AI工具层的视图 public OrderContract toContract() { return new OrderContract(id.value(), status.name(), totalAmount.asBigDecimal(), items.stream().map(OrderItem::toContract).toList(), events.stream().map(DomainEvent::toContract).toList()); } } // AI工具返回的固定JSON契约 public record OrderContract(String orderId, String status, BigDecimal amount, ListItemContract items, ListEventContract recentEvents) {}这是给AI Agent调用的查询订单工具的返回结构。关键点在于AI拿到的永远是契约对象而不是可以直接修改的领域对象。模型想直接改订单状态对不起工具层只暴露了发起审批这样的命令接口没有暴露改status的接口。这样即使模型产生了幻觉它也找不到动手脚的口子。这一层约束就是聚合根思想在AI时代的核心价值——不是组织代码而是划定数据操作的规则和范式。4. 从DDD到AI工程实践建模产物如何转成AI上下文前面聊了DDD概念本身还能用接下来进入实操层怎么把DDD的建模产物转成AI真正能吃进去的东西。这一步是很多团队卡住的地方因为传统的建模输出是给人看的文档而AI需要的是结构化、可调用的上下文。4.1 领域文档、Prompt上下文、MCP工具描述先说说我的具体做法。事件风暴之后我不会急着把结果写成几十页的Word文档而是直接转成三种AI可消费的产物术语表转Prompt词汇约束把领域通用语言中的关键术语和定义整理成一小段固定文本拼进Agent的系统提示词里。比如定义订单状态只允许从PENDING到APPROVED再到COMPLETED不允许跳跃。这段文字每次调用都注入但要控制在几百字以内。领域事件表转工具描述把识别出的领域事件整理成一份结构化清单作为Agent编排的判断依据。本质上就是告诉AI你现在能感知到哪些事件、每个事件发生后你可以做什么。这份清单我会写到工具描述里让模型在决策时参考。限界上下文边界转工具分组这是最关键的一步。任何一个Agent能看到的工具列表都必须按限界上下文分组并且标注本Agent仅允许调用以下分组内的工具。我用MCP协议Model Context Protocol来暴露这些工具每个上下文对应一组MCP server工具的描述里明确写清入参、出参、副作用。举个具体的MCP工具描述示例{ name: query_order_by_id, context: order-context, description: 查询订单详情。仅可查看订单聚合内的信息。不可修改订单状态状态变更请调用 approve_order 或 complete_order。, inputSchema: { type: object, properties: { orderId: { type: string, description: 订单号 } }, required: [orderId] }, outputContract: { type: object, properties: { orderId: { type: string }, status: { type: string, enum: [PENDING, APPROVED, COMPLETED] }, items: { type: array } } } }注意描述里的两个关键句仅可查看订单聚合内信息和不可修改订单状态状态变更请调用xxx。这就是把聚合规则直接写进了模型可感知的边界里。模型看不到代码但它看得到工具描述描述即约束。4.2 测试策略的演变用AI测试开发倒逼建模AI时代测试开发也要改思路。过去我们写单元测试是测特定方法现在面对AI系统同样需要测试但要抓住重点优先保证契约和边界的稳定而不是测试AI的输出内容。我现在做AI项目的测试策略是三层契约测试针对每个暴露给AI的工具接口编写契约测试确保返回的JSON结构永远符合声明。这一层靠代码生成和校验是最硬性的保障。行为测试针对Agent用一组固定的业务场景比如用户申请退款但库存已锁定验证Agent走的路是否合法——它不能越权调用工具不能跳过审批节点。这层测试用自动化测试框架跑跑不过就是Agent的提示词或工具描述有问题。模型回归测试定期用相同的Prompt输入抽查不同模型版本下Agent的输出稳定性和工具选择准确性。有意思的是这层测试体系做完之后反而倒逼了领域建模的完善。因为你要写契约测试就必须把每个工具接口的输入输出定义清楚你要写行为测试就必须把业务流程里的状态流转梳理清楚。AI测试开发不只是测AI它还能帮你检查模型是否理解了这个领域的边界。如果测试经常失败多半不是模型的问题而是你的领域边界定义本身有歧义。4.3 模型部署与Agent运行时的边界最后再说一个传统DDD完全没覆盖、但AI友好型架构里必须处理的点模型部署与运行时的质量保障。DDD管不到模型推理但AI工程实践得管。我遇到过最尴尬的事模型明明选对了工具却在解析工具返回结果时因为多了个嵌套字段就崩溃了。所以我在自己的架构里明确了一条规则模型输出和模型输入之间必须有一层运行时校验。具体做法是Agent在调用任何工具前系统会按JSON Schema校验模型给参数在工具返回给模型之前也会按契约校验返回结构。如果校验失败就重试一次或者直接中断并把异常上报到可观测平台而不是让模型继续拿脏数据往下编。这个运行时校验层本质上就是DDD里防腐层的一种AI化改造。你不把边界设清楚模型的错误输出就会像病毒一样蔓延到整个业务系统。字段多一层嵌套还是少一层模型不会在乎但你的业务会。5. 真实的落地法AI友好型DDD怎么搭最后给一套可以直接抄作业的落地流程。这是我经历了两三个项目踩坑之后沉淀出来的AI友好型DDD搭建步骤。5.1 核心步骤与推荐分层整个搭建过程我只保留DDD里最能解决AI问题的三个东西限界上下文、领域事件、聚合契约。其余那些复杂的领域层设计规范在AI时代大部分场景可以裁剪掉。具体步骤轻量事件风暴一天召集业务方和两个开发在两个小时里梳理出核心业务事件和关键业务规则。产出物只有两样一张事件表事件名、触发条件、后续动作、一张术语表业务词汇的标准定义。不要画巨型流程图不要设计复杂的聚合模型。划分限界上下文半天根据事件表把系统拆成互相独立的上下文。判断标准很简单——哪些业务概念必须同时一致地出现。比如订单和库存有关联但他们的生命周期不同就可以拆成两个上下文。每个上下文暴露为工具/API契约两天把每个上下文的对外能力定义成一组工具用MCP协议主路由暴露出来。入参出参都定好JSON Schema并且用契约测试固化。用Agent编排跨上下文流程一天Agent只存在于每个上下文内部跨上下文操作只能通过领域事件/消息队列异步完成。禁止Agent直接跨上下文调工具这是硬规则。运行时校验全链路可观测持续每个工具调用都经过校验层每次Agent决策链路都记录下来。推荐的分层结构大概是这样的最底层是领域模型和领域服务传统DDD代码层中间层是工具契约层把领域能力包装成AI可调用的结构化接口最外层是Agent运行时和模型接入层。核心原则业务知识必须留在最底层AI只消费契约层永远不直接碰数据表。5.2 踩坑记录三个典型翻车案例说过理论再分享三个真实翻车案例都是我项目里踩过的。每个讲完配一句教训。案例一把数据库表直接暴露给LLM模型每次都查全表。我们的Agent接了个查订单工具参数只有订单号但模型经常不传参数直接全表扫描。后来才发现工具描述写得模糊没告诉模型必须先有orderId才能调用没有订单号时先询问用户。教训是工具描述是给模型看的语义边界必须写得像给新员工写的工作手册一样清楚。案例二聚合规则和提示词双份维护改了一边漏一边。团队把满100免运费规则既写在代码里又写在Prompt里提醒AI。某次改规则只改了代码Prompt没同步结果AI生成的话术和实际价格对不上。教训一个规则只允许存在一处领域规则放代码Prompt只放术语和边界不要重复。推荐在事件表里标清哪条业务规则由代码守护、哪条由Prompt守护避免维护混乱。案例三一个Agent挂了50个工具上下文爆掉选择混乱。后来我们强制要求一个Agent的工具数量不超过20个并且全部按限界上下文分组。超出部分要么拆Agent要么对模型隐藏。教训工具数量决定了模型的选择成本工具组就是你的限界上下文要学会克制。5.3 什么时候可以放弃DDD说了这么多我也得诚实不是所有场景都需要AI友好型DDD。有些系统确实可以直接放弃判断标准我给三条大家可以对号入座业务规则极简单没有复杂的生命周期和状态流转比如一个纯展示型CMS、一个接口中转层表驱动就够了硬上DDD是给自己找麻烦。系统一次性脚本或内部工具只有几个人用生命周期几个月不值得建边界。完全不需要AI多次调用和长期维护如果只是一个单次问答型应用不涉及跨系统协作那领域模型的意义也很有限。反过来只要系统满足这三条中的任意一条就值得认真引入DDD思维一是业务规则会频繁变化二是多团队、多系统协作边界不清会互相踩踏三是系统里有不可预测的状态流转比如订单状态机、审批流。我在实际项目里见过太多反面案例了。有一次帮朋友救火接手一个半年内迭代了三次规则的系统前任为了省事把业务逻辑全写在Prompt里结果每次改规则都要重新调模型失败率极高。后来我们把那些规则全部沉淀回代码层AI只负责解释规则、辅助用户系统一下子就稳了。这就是AI时代DDD的真相不是取代程序员而是给AI和代码之间划一条清晰的边。写在最后回顾我过去一年的AI项目经历最大的体会是AI让编程的体力活变便宜了但让概念设计的价值变得更高。以前DDD是防止代码腐烂现在DDD是防止提示词腐烂和Agent失控。没有边界意识的AI系统就像一匹没有缰绳的马跑得快但随时可能带着你冲下悬崖。如果你现在正好在纠结要不要给AI项目引入DDD我的建议是别纠结先拿出一张白纸用半天时间梳理你系统里有哪些业务事件、有哪些必须同时一致出现的概念、有哪些能让Agent自由操作的接口然后照着上面的步骤去搭一个轻量边界出来。你会发现一次清晰的三层拆分比调十轮Prompt都管用。AI不会替代DDD但DDD的形态一定会变。至于怎么变我们这些做工程的人正在用一次次的实战给出答案。
返回列表