领域驱动设计(Domain-Driven Design,简称 DDD)是由 Eric Evans 在 2003 年提出的架构思想。
作为资深 Java 开发,你可能发现传统的“三层架构”(Controller-Service-DAO)在业务逻辑极其复杂时(如你之前参与的教育系统或交易系统),容易变成**“贫血模型”**:Service 逻辑几千行,数据库表几百个,改一个功能牵一发而动全身。
DDD 的核心就是:拒绝以数据库为中心,回归以业务逻辑为中心。
1. 战略设计:划清边界(高层视角)
在动手写代码前,先通过业务专家和技术团队的“语言对齐”,确定大版图。
-
统一语言 (Ubiquitous Language):拒绝“程序员黑话”。代码里的变量名、类名必须和业务人员口中的名词一致(例如:教育业务里叫“排课”,代码里不能叫
TaskSchedule,而应叫CourseArrangement)。 -
限界上下文 (Bounded Context):这是 DDD 最核心的贡献。
-
例子:在“订单上下文”里,
User是买家,关注收货地址;在“权限上下文”里,User是账号,关注角色和密码。同一个实体在不同上下文中应该拆分开,避免一个User类变成万能补丁。
-
-
上下文映射 (Context Mapping):定义不同边界之间如何通信(是通过 RPC、MQ 还是共享库)。
2. 战术设计:代码实现(落地视角)
这是你作为资深开发在 Java 工程中具体写代码的方式:
-
实体 (Entity):具有唯一标识(ID),且状态会随时间改变。
-
值对象 (Value Object):没有唯一标识,仅表示某种属性(如:地址、金额)。不可变性是它的灵魂,修改时直接替换整个对象。
-
聚合根 (Aggregate Root):一组关联对象的负责人。
-
原则:外部对象只能通过聚合根来修改内部成员。比如“订单”是聚合根,“订单项”是内部成员,你不能直接给订单项改价格,必须通过订单对象来操作,以保证业务逻辑的一致性。
-
-
领域服务 (Domain Service):有些逻辑不属于任何一个实体(比如转账操作涉及两个账户),就放在领域服务里。
-
仓储 (Repository):不同于 DAO。DAO 是对数据库表的封装,Repository 是对聚合根的持久化封装。它屏蔽了底层是 MySQL 还是 Redis 的细节。
3. 架构演进:六边形架构 (Hexagonal Architecture)
DDD 通常配合“整洁架构”或“六边形架构”使用,将核心业务逻辑保护在最里面。
-
内层:领域模型、领域服务(纯 Java,不依赖任何框架)。
-
外层(适配器):数据库驱动、REST 接口、消息队列。
-
好处:如果你想把数据库从 MySQL 换成 MongoDB,或者把 Dubbo 换成 Spring Cloud,你只需要改外层的适配器,核心业务代码一行都不用动。
4. 为什么资深开发都要学 DDD?
-
应对复杂性:当逻辑复杂到人脑记不住时,DDD 的“拆分”和“边界”是唯一的救命稻草。
-
微服务拆分标准:很多人拆微服务是按功能拆,结果拆成了“分布式单体”。DDD 的限界上下文才是微服务拆分的天然边界。
-
职业转型:正如你想做产品或架构,DDD 让你学会用业务的视角看技术,这是从“码农”向“架构师”跨越的关键。
🛠 实践建议(避坑指南)
-
不要“为了 DDD 而 DDD”:如果只是一个简单的增删改查系统,用三层架构最快。强行上 DDD 会多出大量的 DTO、VO 转换,导致过度设计。
-
充血模型 (Rich Domain Model):尽量把逻辑写在实体类里,而不是全部塞进 Service。让对象自己“动起来”。
📉 状态同步
-
2026-03-29:已梳理 DDD 战略设计(限界上下文)与战术设计(聚合、实体、仓储)的核心逻辑。
既然聊到了领域驱动设计,你想看看一个标准的 Java 工程结构(例如:api, application, domain, infrastructure)应该如何组织代码吗?这能让你瞬间看清 DDD 的落地形态。