news 2026/7/24 1:07:20

Java DDD(领域驱动设计)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java DDD(领域驱动设计)

一句话核心:DDD(领域驱动设计)不是一套框架,而是一种代码组织哲学。它要求你用代码直接翻译业务语言,而不是把业务逻辑散落在增删改查(CRUD)里。

对于Java开发者,最经典的DDD范式就是**“四层架构” + “六大战术模式”。为了让你秒懂,我们把项目类比为“一家餐厅”**:

  • 表现层(Controller)= 服务员(接收顾客指令)。
  • 应用层(Application)= 大堂经理(调度任务,发号施令)。
  • 领域层(Domain)= 厨师长 + 菜谱(核心资产,业务规则所在)。
  • 基础设施层(Infrastructure)= 冰箱、灶台(数据库、Redis、MQ)。

下面我直接给你展示标准的Java DDD包结构核心代码范式,一看就懂。


1. 标准的Java DDD包结构(范式骨架)

com.example.order/ ├── interfaces/ # 表现层(处理HTTP/RPC) │ └── OrderController.java ├── application/ # 应用层(用例编排,无业务逻辑) │ ├── service/ # 应用服务(如:OrderApplicationService) │ └── dto/ # 数据传输对象(Request/Response) ├── domain/ # 领域层(核心!只依赖Java本身,不依赖Spring/DB) │ ├── model/ # 领域模型 │ │ ├── aggregate/ # 聚合根(如:Order) │ │ ├── entity/ # 实体(如:OrderItem) │ │ └── vo/ # 值对象(如:Address, Money) │ ├── service/ # 领域服务(处理跨多个实体的复杂业务) │ └── repository/ # 仓储接口(定义“我需要什么数据”,不实现) └── infrastructure/ # 基础设施层(实现Domain层的接口) ├── repository/ # 仓储实现(JPA/MyBatis) └── config/ # 配置类

2. 六大战术模式(Java代码实战)

为了便于理解,我们模拟一个**“下单”**场景:必须验证地址,且订单总价要打折。

① 值对象 (Value Object) —— 用record或不可变类

特点:没有唯一ID,通过属性值判断相等性。封装业务规则

// 值对象:地址publicrecordAddress(Stringprovince,Stringcity,Stringdetail){publicAddress{// 业务规则:地址不能为空,直接写在构造方法里if(province==null||city==null){thrownewIllegalArgumentException("省市必填");}}}// 值对象:金额publicclassMoney{privatefinalBigDecimalamount;privatefinalStringcurrency;// 通常用枚举// 封装加法逻辑,而不是在Service里用BigDecimal算publicMoneyadd(Moneyother){...}}
② 实体 (Entity) —— 有唯一ID,可变
@Entity// 注意:这里的注解是JPA的,但DDD要求Domain层纯POJO,所以通常把这个类放在infrastructure层做映射,或者在Domain层不加@Table注解。publicclassOrderItem{privateLongitemId;// 唯一标识privateStringproductName;privateIntegerquantity;privateMoneyprice;// 引用值对象}
③ 聚合根 (Aggregate Root) ——DDD最重要的范式

聚合根是外部访问聚合的唯一入口。比如“订单(Order)”和“订单项(OrderItem)”是整体,外部只能操作Order,不能直接修改OrderItem

// 领域层 - 聚合根publicclassOrder{privateLongorderId;// 唯一标识privateAddressshipAddress;// 值对象privateList<OrderItem>items;// 实体集合privateOrderStatusstatus;// 核心业务方法(充血模型):业务逻辑写在实体内部,而不是Service里!publicvoidplaceOrder(){// 规则1:下单必须校验地址(封装在领域内部)if(this.shipAddress==null){thrownewBusinessException("缺少收货地址");}// 规则2:计算总价并打折(满100减10)Moneytotal=calculateTotal();if(total.amount().compareTo(newBigDecimal("100"))>=0){// 打折逻辑...}this.status=OrderStatus.PLACED;// 发布领域事件(可选)DomainEventPublisher.publish(newOrderPlacedEvent(this.orderId));}// 计算总价也不允许Service插手,由实体自己算privateMoneycalculateTotal(){returnitems.stream().map(OrderItem::getPrice).reduce(Money::add).orElse(...);}}
④ 仓储接口 (Repository) ——依赖倒置的核心

Domain层定义“我要什么”,Infrastructure层实现“怎么拿”

// domain/repository/OrderRepository.java (接口)publicinterfaceOrderRepository{OrderfindById(Longid);voidsave(Orderorder);// 传入聚合根,整个聚合一起保存}// infrastructure/repository/OrderRepositoryImpl.java (实现)@RepositorypublicclassOrderRepositoryImplimplementsOrderRepository{@AutowiredprivateOrderJpaMappermapper;// MyBatis或JPA@Overridepublicvoidsave(Orderorder){// 1. 保存Order主表// 2. 保存OrderItem子表(全部替换策略)// 领域层不知道底层是MySQL还是Redis}}
⑤ 领域服务 (Domain Service)

当业务逻辑不属于某个具体的实体时使用(比如转账涉及两个账户)。

// domain/service/TransferService.javapublicclassTransferService{publicvoidtransfer(Accountfrom,Accountto,Moneyamount){from.debit(amount);// 实体自己的方法to.credit(amount);}}
⑥ 应用层 (Application Service) ——极薄的一层

只做三件事:拿数据、调领域方法、存数据。绝对不写 if-else 业务判断

// application/service/OrderAppService.java@Service@TransactionalpublicclassOrderAppService{privatefinalOrderRepositoryorderRepository;// 用例:用户下单publicLongplaceOrder(PlaceOrderCommandcmd){// 1. 从仓储拿聚合根(如果是新建则new)Orderorder=newOrder();// 2. 调用领域方法(业务逻辑进入领域层)order.placeOrder();// 3. 仓储保存orderRepository.save(order);// 4. 返回结果returnorder.getOrderId();}}

3. 必须遵守的“铁律”(DDD Java范式红线)

层级是否允许依赖Spring注解是否允许依赖数据库职责
领域层 (Domain)❌ 禁止(纯Java POJO)❌ 禁止只写interface定义规则,写业务逻辑方法
应用层 (Application)✅ 允许 (@Service)❌ 禁止编排用例(调用领域方法),事务发起点
基础设施层 (Infra)✅ 允许 (@Repository)✅ 允许实现接口,处理SQL、Redis、MQ
表现层 (Interfaces)✅ 允许 (@RestController)❌ 禁止参数校验、返回JSON

为什么领域层禁止用Spring注解?
因为DDD追求**“业务逻辑与技术解耦”**。如果@Autowired进了Domain,换用Quarkus或单元测试时就废了。Domain应该是可以脱离Spring,直接用new Order().placeOrder()就能跑单元测试的。


4. 贫血模型 vs 充血模型(一眼辨别真假DDD)

  • 传统CRUD(贫血)Order类只有getter/setter,所有if/else全在OrderServiceImpl里。→这是假DDD
  • DDD范式(充血)Order类拥有placeOrder()cancel()calculateTotal()方法。OrderAppService只有寥寥几行调用。→这是真DDD

总结(记住这张脑图)

表现层接收请求 →应用层基础设施层拿到仓储接口的实现 → 交给领域层聚合根执行业务方法(内部包含实体和值对象的规则) → 应用层再通过仓储存回去。

如果只是简单的增删改查,请不要用DDD(杀鸡用牛刀)。但如果业务逻辑极其复杂(如电商、金融、医疗),这个Java范式就是防止代码腐烂的最强武器。记住核心口诀:“让代码说业务的话,让数据库滚去基础设施层”

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/24 0:18:02

本地大模型部署全攻略:从Ollama到企业级容器化

1. 本地大模型部署方式全景解析在AI技术快速发展的当下&#xff0c;本地部署大模型已成为企业数字化转型和个人开发者探索AI能力的重要选择。与云端API调用相比&#xff0c;本地部署能够提供更高的数据隐私性、更低的长期使用成本以及更灵活的定制空间。目前主流的本地部署方式…

作者头像 李华
网站建设 2026/7/24 0:02:34

HarmonyOS开发实战:小分享-ShareCard通用分享卡片组件设计

前言 通用组件 是提升代码复用率的核心手段。ArkUI 通过 Component 装饰器让开发者可以封装自定义组件&#xff0c;支持 Prop 参数化输入。本篇以小分享 App 的 ShareCard 组件为例&#xff0c;讲解通用组件的设计原则和 Prop 装饰器的使用。详细 API 可参考 HarmonyOS Compon…

作者头像 李华
网站建设 2026/7/24 0:01:05

Django毕设项目:基于 Django 的 智能化学生综合素质测评审核系统 校园学生评优评奖综合管理系统(源码+文档,讲解、调试运行,定制等)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华