领域驱动设计

领域驱动设计(DDD)不是一种编程技术,而是一种软件建模方法。 它的核心思想是:代码的结构和语言,必须严格对应业务专家(客户)心中的业务模型和语言。


1. 通俗比喻:建筑设计师 vs. 结构工程师

假设你要盖一栋别墅:

  • 业务专家(客户)说:“我要一个宽敞的‘起居室’,一个‘开放式厨房’,还要一个能看星星的‘阳光房’。”

  • 传统开发:程序员听完,转头就在数据库里建了三张表(Table_A, Table_B, Table_C),用一堆技术代码去实现增删改查。过几个月,客户来看,发现“阳光房”变成了“杂物间”,因为代码里根本没有“阳光房”这个概念,只有一堆数据流转。

  • 领域驱动设计(DDD):软件架构师会坐下来和客户一起画图,说:“您的‘起居室’有哪些行为?能容纳多少人?‘阳光房’的屋顶开合逻辑是什么?”最后,代码里会有一个名为 Sunroom(阳光房)的Java类,它拥有 openRoof() 和 closeRoof() 方法。 代码里的名词和客户嘴里的名词完全一致。

结论: DDD的终极目标,是让代码模型成为业务领域的精确镜像,让即便不懂技术的业务人员也能看懂核心代码的逻辑。


2. DDD的两大“杀手锏”(核心战术)

为了做到上述映射,DDD提供了两套强大的工具:

第一套:战略设计(处理“大问题”)—— 划分地盘

  • 限界上下文(Bounded Context):这是DDD最重要的概念。它明确划定了某个词汇在哪个范围内是什么意思。例如,“账户”在“电商上下文”里是登录名,在“财务上下文”里是银行卡号。通过画“限界上下文”,团队能清晰划分出微服务的边界。

  • 通用语言(Ubiquitous Language):开发人员、产品经理、业务专家必须说同一种词汇。不能在开会时说“用户点击支付”,写代码时却命名为 PaymentProcessor,必须统称为“支付”。

第二套:战术设计(处理“小问题”)—— 落地代码

  • 实体(Entity):有唯一ID、会变状态的对象。比如订单,订单号不变,但状态会变。

  • 值对象(Value Object):没有ID,只靠属性值来定义的对象。比如地址,只要省市区街道一样,就是同一个地址,不需要单独ID。换掉直接替换即可。

  • 聚合(Aggregate):一组相关对象的“组合包”,通过一个聚合根来统一管理外部访问。例如,订单是聚合根,要修改订单行或收货地址,必须通过订单这个聚合根去操作,以此保证数据的一致性(这也很适合做事务边界)。


3. 什么时候该用DDD?(切记:不是万能的)

  • ✅ 适合用DDD的场景:业务逻辑极其复杂(如保险计费、金融风控、供应链排期)、规则经常变化、且业务专家和开发人员都搞不清逻辑的“核心业务域”。DDD帮你把复杂的业务“理清楚”。

  • ❌ 不适合用DDD的场景:简单的CRUD(增删改查)(如博客后台、简单的管理系统)。这种场景用DDD只会增加大量不必要的类,属于“过度设计”。这种时候,用贫血模型+MyBatis反而更高效。


4. 最容易踩的坑(避雷指南)

  1. DDD ≠ 微服务:很多人为了做微服务才学DDD。但其实DDD是用来划分边界的,微服务是部署方式。你可以用DDD设计出一个单体架构,也可以不用DDD却搞出十几个杂乱无章的微服务。

  2. 不要在“数据表”上建DDD:很多程序员拿着MySQL表结构,直接给每张表生成一个实体类,这不是DDD。DDD是行为驱动的,先想“这个对象能做什么”,再想“它存什么数据”,而不是先设计数据库。


5. 极简示例(让你秒懂代码差异)

非DDD(贫血模型):

// 只有get/set,没有行为
class Order {
    private Long id;
    private String status;
    // getter and setter...
}

// 业务逻辑写在Service里
service.cancelOrder(orderId) {
    order.setStatus("CANCELLED");
    // 还要去改库存...
}

 

DDD(充血模型):

// 数据和行为在一起
class Order {
    private Long id;
    private OrderStatus status;
    
    // 业务逻辑封装在实体内部
    public void cancel() {
        if (this.status == OrderStatus.SHIPPED) {
            throw new Exception("已发货不能取消");
        }
        this.status = OrderStatus.CANCELLED;
        // 发布领域事件:OrderCancelledEvent
    }
}

// 使用时
order.cancel(); // 简单直白,符合业务语言

 



总结

领域驱动设计的本质是“翻译”——把业务专家大脑里晦涩、模糊的专业知识,翻译成清晰、可维护的代码模型。它要求你深入业务,而不是整天盯着数据库。

如果你正在学习,建议从“通用语言”和“限界上下文”这两个概念入手,别一上来就死磕仓储模式或事件溯源。先画好边界,比写好代码更重要。

posted @ 2026-08-13 18:19  古锁阳关  阅读(23)  评论(0)    收藏  举报