领域驱动设计:AI编程的隐藏王牌
当你的AI助手读不懂你的时候
说句实话:你肯定在某个时刻被AI编程助手气到过。你提了个需求,它给出一段技术上能跑但完全没get到点的代码。类名取得平平无奇,逻辑分散在各种Helper类里,完全无视你那套独特的业务规则。
这里有个扎心的事实:问题不在AI,在你。
不是说甩锅给你——而是说你得从实际角度理解这件事。AI语言模型在模式匹配和代码生成方面确实很强,但它的能力有边界,边界就是你给它的上下文。你给AI模糊的上下文,它就给你模糊的代码;你给模糊的指令,它就给你模糊的行为。
这就引出了Domain-Driven Design(DDD),说实话,这是自代码补全以来,对AI辅助开发最有用的东西了。
DDD到底是什么(不搞学术那套)
我知道你可能在想:"DDD?是不是那个重得要命的方法论,有什么47种模式和一本800页的蓝皮书?"
对,也不对。
DDD本质上是在赌软件复杂度的来源。它的前提是:大多数应用里,难点不在技术,而在对业务领域的理解和建模。业务规则、在特定场景下才有意义的术语、只有结合上下文才能理解的边界情况。
所以DDD说:把你业务领域的模型放在工作的核心位置。用一种开发者和业务人员都能理解的语言来表达这个模型。让代码直接反映这个模型。
战略层面讲的是大局:建立一套共享词汇(叫做Ubiquitous Language,通用语言),定义清晰的边界(Bounded Contexts,限界上下文),让这套词汇在边界内保持一致。
战术层面讲的是具体的构件:有身份标识的实体(Entity)、由属性定义的Value Object、聚合相关对象为一组的聚合根(Aggregate),以及记录有意义变化的领域事件(Domain Event)。
要特别警惕一个反模式——"贫血模型"。这就是说你的对象只是一堆数据袋,有getter有setter,真正的逻辑全跑到外面的Service类里去了。DDD对这种做法是强烈反对的:把行为放到数据所在的地方去。
为什么这对AI编程助手很重要
这里就是有意思的地方了。
AI语言模型本质上就是超级增强版的模式补全引擎。它们根据训练数据里见过的代码来预测"代码应该长什么样"。问题就在这里:训练数据里充斥着贫血CRUD模式、通用命名、分散的逻辑。因为大多数代码库走的都是这条最容易的路,所以模型也会默认生成这样的代码。
当你运用DDD原则时,你实际上是在给AI设置护栏,引导它产生更好的结果。你创建的每一个DDD产物都是prompt素材。你做出的每一个明确决策都成了AI可以工作的约束条件。
让我说说最关键的四个方面:
1. 你建立的共享词汇成了AI的系统提示词
想象一下:你在做一个订阅管理系统。你的团队已经达成共识,"订阅"、"套餐"、"权益"这三个词有非常具体的含义。订阅是客户和套餐之间的活跃关系。套餐定义了可以提供什么内容。权益是客户实际能使用的东西。
当你在写用户故事或功能描述时使用这些精确的术语,AI就能内化这套词汇。你让它加一个"升级订阅"的功能,它生成的代码就会尊重这些区别。它不会把"套餐"和"权益"混为一谈,因为你对它们各自的角色说得很清楚。
你维护的那份给新开发者入职用的术语表?恰好也是AI需要的东西。同样的信息,一份两份用。
2. 限界上下文让AI的对话保持专注
你有没有试过让AI帮你做一个涉及系统六七个不同部分的功能?结果通常很乱——改了这里漏了那里,各模块间的逻辑互相矛盾。
限界上下文天然解决了这个问题。每个上下文都是一个明确的区域,有自己的一套模型和术语。当你在"计费"上下文工作时,"账户"这个词的含义可能和"用户管理"上下文里的不一样。只要边界清晰,这完全没问题。
对于AI工作流来说,这完美对应上了专注的对话。"今天我们在订单履约上下文工作。这是它的词汇和规则。暂时忽略库存上下文。"这就是让模型保持专注而不是到处乱窜的方法。
3. 行为丰富的模型保护你的业务规则
这是经常被忽视的实际安全收益。
当你把业务规则放到拥有相关数据的对象里面,就只有一个地方需要守卫它们。当这些规则分散在多个Service类里,任何生成的代码都可能不小心绕过它们。
想想订单验证规则:没付款的订单不能发货。如果这个规则在Order.Ship()里,就是受保护的。任何想发货未付款订单的代码都得经过这个检查。但如果规则在单独的ShippingService里,AI生成的代码可能很乐意搞一个新的ShippingProcessor,发订单的时候根本不检查付款状态。
行为丰富的模型把你的不变量集中在真正能保护系统的地方。
4. 测试就是可执行的规格说明
DDD强调不变量——必须始终保持为真的规则——这直接转化为测试用例。"订阅取消后不能续订。""用户转出的积分不能超过持有的数量。""地址不完整的订单不能发货。"
这些不只是好的测试用例。它们是AI可以使用正确性定义。先把这些写好,AI就有了一个具体的靶子来对着编码。它可以跑测试,立刻知道自己是不是走对了路。这比指望AI看懂业务规则的自然语言描述要靠谱得多。
需要避开的坑
我得跟你说清楚:把DDD和AI结合用不是点一下就自动出奇迹。有些具体的坑你得知道。
AI默认会生成贫血的CRUD。 如果你说"写一个Customer类",你通常会得到一堆public getter/setter,逻辑全被流放到外面的Service。你得明确要求对象上的行为、私有setter、逻辑要和它保护的数据待在一起。
AI会自创词汇。 你说"订单",它写"交易"。你说"订阅",它写"会员"。在你这个领域里这不是同义词,但AI不知道,除非你告诉它。在prompt里保持一致的、明确的词汇有帮助,但更重要的是把这些术语放到文件名、类名、注释里,AI会自然而然地学到它们。
AI不确定的时候会过度设计。 AI对正确的复杂度没有清晰指导时,往往会默认用上花哨的模式——抽象工厂、过多的接口、不必要的层次。DDD其实在这里提供了一个有用的判断标准:用那些"贵"的模式(聚合根、领域事件、限界上下文)只在业务复杂度值得的时候。对于简单的领域,有清晰命名的行为丰富类可能就够了。
务实的建议
想了这么多之后,我给你一个诚实的评价:DDD现在已经不只是一套编码方法论了。它是AI时代的上下文工程方法论。
你采用的每一个DDD实践都让你的领域变得更明确,而"明确"恰恰是语言模型能够行动的基础。你为团队建立的共享词汇成了AI使用的词汇。你划定的限界上下文成了AI专注工作时自然的单元。你创建的行为丰富的模型成了业务规则不会被悄悄绕过的受保护空间。
本来你就应该做这些工作,如果你想要可维护的软件的话。现在它和AI助手配合也能产生收益了。
DDD里"便宜"的部分——语言清晰和行为集中——适用于任何地方。那些"贵"的部分——完整的战术模式、花哨的事件溯源——只在领域复杂度确实值得的时候才用。
先从便宜的部分开始。把词汇弄明确。把行为和数据放一起。保持边界清晰。然后让AI帮你实现剩下的部分。
你未来的自己,半夜两点调试代码的时候会感谢你的。AI也会的——那个真正帮得上忙而不是帮倒忙的AI。