🏰 前言:DDD 虽好,但门槛太高?
在后端架构圈,DDD (领域驱动设计)被誉为解决复杂业务逻辑的“圣杯”。
但现实往往是骨感的:
- 概念晦涩:限界上下文、聚合根、实体、值对象、领域服务……光是背名词就劝退了一半人。
- 落地困难:开了一天的 Event Storming(事件风暴)会,最后写出来的代码还是MVC + CRUD的贫血模型。
- 成本高昂:需要极具经验的架构师才能划分清楚边界。
如果我告诉你,现在的 AI 大模型(如 DeepSeek/GPT-4)已经读懂了 Evans 的《领域驱动设计》全书,并且能帮你自动完成建模和代码生成呢?
今天,我们就来尝试用AI 辅助 DDD,让原本需要一周的建模工作,压缩到 10 分钟!
🧠 核心思路:AI 是你的“领域专家”
在传统 DDD 中,我们需要拉着业务方不断沟通以提炼“统一语言”。
现在,我们可以把需求文档投喂给 AI,让它利用强大的语义理解能力,帮我们识别出实体 (Entity)和值对象 (Value Object),并构建出聚合 (Aggregate)。
AI 辅助建模工作流:
🛠️ 实战演练:重构“电商订单系统”
假设我们有一段混乱的业务描述:
“用户下单时,需要包含多个商品,每个商品有价格和数量。订单需要有收货地址(省市区街道)。如果商品总价超过 200 元,订单状态变为‘已确认’,否则是‘待审核’。收货地址一旦创建不可修改,但可以整个替换。”
步骤 1:让 AI 识别 Aggregate 和 Value Object
Prompt (提示词):
你是一位 DDD 专家。请根据上述需求,识别出聚合根、实体和值对象。
要求:
- 地址(Address)和金额(Money)必须设计为 Value Object。
- 订单(Order)是聚合根。
- 请用 Mermaid Class Diagram 画出类图。
AI 输出的分析:
- Order (聚合根):负责维护订单状态和总价计算的不变性(Invariant)。
- OrderItem (实体):订单内部的条目。
- Address (值对象):无生命周期,不可变。
- Money (值对象):避免使用 double,封装金额计算逻辑。
步骤 2:生成“充血模型”代码
我们要求 AI 生成 Java 代码,并明确禁止生成 Getter/Setter 的贫血模型。
Prompt:
请生成 Java 代码。要求:
- 使用充血模型,业务逻辑在对象内部。
- 只有必要的 Getter,严禁 Setter。
- 使用
record实现 Value Object。- 实现
createOrder和addItem等业务方法。
AI 生成的代码(惊艳):
1. 值对象 (Value Object) - 坚如磐石
// Money.java - 典型的 Value Object,不可变且包含逻辑publicrecordMoney(BigDecimalamount,Stringcurrency){publicMoney{if(amount.compareTo(BigDecimal.ZERO)<0){thrownewIllegalArgumentException("金额不能为负");}}publicstaticMoneyof(doubleamount){returnnewMoney(BigDecimal.valueOf(amount),"CNY");}publicMoneyadd(Moneyother){if(!this.currency.equals(other.currency)){thrownewIllegalArgumentException("币种不一致");}returnnewMoney(this.amount.add(other.amount),this.currency);}}// Address.javapublicrecordAddress(Stringprovince,Stringcity,Stringstreet){// 地址是不可变的,替换地址意味着创建一个新的 Address 对象}2. 聚合根 (Aggregate Root) - 业务逻辑的守门员
// Order.javapublicclassOrder{privateOrderIdid;privateList<OrderItem>items;privateAddressshippingAddress;// 引用值对象privateOrderStatusstatus;// 构造方法私有,通过工厂方法创建privateOrder(OrderIdid,Addressaddress){this.id=id;this.shippingAddress=address;this.items=newArrayList<>();this.status=OrderStatus.PENDING_REVIEW;}// 业务行为:添加商品// 逻辑内聚:外部无法直接操作 items 列表,保证数据一致性publicvoidaddItem(Productproduct,intquantity){if(quantity<=0){thrownewDomainException("数量必须大于0");}this.items.add(newOrderItem(product,quantity));// 触发领域规则检查checkAmountRule();}// 领域规则:总价 > 200 自动确认privatevoidcheckAmountRule(){Moneytotal=calculateTotal();if(total.amount().compareTo(BigDecimal.valueOf(200))>0){this.status=OrderStatus.CONFIRMED;}}publicMoneycalculateTotal(){returnitems.stream().map(OrderItem::subtotal).reduce(Money.of(0),Money::add);}}💥 对比:AI 写的 vs 人写的
- 人写的(贫血模型):
通常是一个@Data注解走天下,业务逻辑散落在 Service 层的各种if-else里,Order 对象只是个数据容器,随便谁都能setTotalAmount(),极其不安全。 - AI 写的(充血模型):
Order对象保护了自己的内部状态。你无法设置一个错误的金额,也无法绕过“满200确认”的规则。代码即业务文档。
🛡️ 最佳实践指南
虽然 AI 能生成漂亮的代码,但 DDD 的落地还需要注意:
- 上下文映射 (Context Mapping):
AI 容易忽视模块间的边界。你需要明确告诉它:“订单上下文”和“会员上下文”是通过 ID 关联还是通过 ACL 防腐层交互。 - Repository 接口设计:
让 AI 生成 Repository 接口时,要强调**“面向集合的设计风格”**(如add,remove),而不是面向数据库的 DAO 风格(insert,update)。 - Prompt 迭代:
不要指望一次 Prompt 就完美。你需要扮演“质疑者”的角色:“如果这里并发修改怎么办?” AI 就会补充乐观锁(Version)的逻辑。
📝 总结
DDD 之所以难,是因为它要求开发者同时具备抽象思维和业务理解力。
而 AI 恰恰填补了这块短板。
通过 AI 辅助,我们不再是从零开始写代码,而是站在一个“虚拟架构师”的肩膀上进行微调。
让 AI 处理繁琐的建模细节,让人回归对核心业务价值的思考。这就是 DDD 在 AI 时代的正确打开方式。