发布时间:2026/7/24 1:08:14
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(provincenull||citynull){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;// 值对象privateListOrderItemitems;// 实体集合privateOrderStatusstatus;// 核心业务方法充血模型业务逻辑写在实体内部而不是Service里publicvoidplaceOrder(){// 规则1下单必须校验地址封装在领域内部if(this.shipAddressnull){thrownewBusinessException(缺少收货地址);}// 规则2计算总价并打折满100减10MoneytotalcalculateTotal();if(total.amount().compareTo(newBigDecimal(100))0){// 打折逻辑...}this.statusOrderStatus.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或JPAOverridepublicvoidsave(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.javaServiceTransactionalpublicclassOrderAppService{privatefinalOrderRepositoryorderRepository;// 用例用户下单publicLongplaceOrder(PlaceOrderCommandcmd){// 1. 从仓储拿聚合根如果是新建则newOrderordernewOrder();// 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范式就是防止代码腐烂的最强武器。记住核心口诀“让代码说业务的话让数据库滚去基础设施层”。

相关新闻

2026/7/24 1:08:14

MSPM0微控制器NONMAIN配置详解:从安全启动到量产避坑指南

1. 项目概述:为什么你需要关注MSPM0的NONMAIN配置?如果你正在使用德州仪器(TI)的MSPM0 L系列微控制器,并且你的产品即将进入量产阶段,或者你正在设计一个对安全性、可靠性和知识产权保护有要求的嵌入式系统…

2026/7/24 1:03:14

羽球搭子 HarmonyOS 实战(19):账号认证后的数据作用域

一、登录成功之后,真正困难的是“不串数据” 球友甲登录后创建了一场周末对局,退出账号,再让球友乙登录同一台设备。如果页面仍然显示甲的对局、邀请码或个人胜率,认证虽然成功,数据边界却已经失效。账号系统不能只回…

2026/7/24 2:38:18

学生党请注意!2026最新3款高口碑实用英语词汇学习软件推荐

核心要点: 1. 本次推荐全部基于2026年1-6月公立校实测使用数据,无任何商业合作;2. 重点拆解AI驱动类词汇工具的核心技术差异,避免盲目跟风选品;3. 针对不同学习场景给出适配建议,主打低时间成本提效。先聊聊…

2026/7/24 2:38:18

西安驼铃传奇演出票行业定价标准及购买渠道科普解读

导语旅游出行中,观看一场精彩的演出是很多游客的选择。在西安,驼铃传奇演出备受关注,其演出票的行业定价标准与购买渠道,是不少游客关心的问题。西安古都艺票通深耕西安本地文旅票务领域,对这些知识有着专业的了解。接…

2026/7/24 2:38:18

量子计算如何加速图像分类?PQCNN架构解析

1. 量子计算与图像智能的融合突破量子计算正在重塑传统图像处理的技术范式。在图像分类领域,传统卷积神经网络(CNN)面临参数爆炸、计算复杂度高等瓶颈,而量子计算的并行性优势恰好能破解这一困局。微美全息(WIMI&#…

2026/7/24 2:38:18

Agent 工程思考:从 ReAct 到 Agent Harness

作者:vivo 互联网项目团队- Ding Junjie 从 ReAct 出发,文章讨论 Agent 工程如何从“模型循环”走向 Harness:通过前后端共享的 State Schema、运行事实和 UI 边界,让模型行为变成可交互、可恢复、可控制、可追溯的产品能力。1分钟…

2026/7/24 2:38:18

论文查重工具评测与AI智能改写技术解析

1. 论文查重工具现状与核心需求解析在学术写作和毕业季高峰期,论文查重服务已成为学生群体的刚需。根据2023年高校学术规范调查报告显示,超过87%的本科生在论文提交前会进行至少3次查重检测。面对市场上数十种查重工具,学生群体普遍面临三个核…

2026/7/24 2:33:18

YOLO26与ACmix注意力机制融合优化目标检测

1. YOLO26与注意力机制融合的背景与价值目标检测领域近年来最显著的突破之一,就是注意力机制与卷积神经网络的深度融合。作为YOLO系列的最新迭代,YOLO26在保持实时检测优势的同时,通过引入ACmix这类混合注意力模块,实现了特征提取…

2026/7/23 12:54:51

Unity与Python本地通信:基于Flask的跨语言数据交换实战

1. 项目概述:为什么我们需要一个本地通信服务器?在游戏开发、数字孪生、仿真训练等众多领域,Unity作为强大的实时3D内容创作平台,其核心逻辑通常由C#驱动。然而,当我们需要进行复杂的数据分析、机器学习推理、科学计算…

2026/7/24 0:03:10

不同品牌斜齿行星减速机如何替换?以PX与PAG系列为例

不同品牌斜齿行星减速机如何替换?以 PX 与 PAG 系列为例 一、系列对应不等于型号直接互换 PX 与 PAG 都属于斜齿、方法兰、输出轴式精密行星减速机,结构形式和应用方向具有对应关系。 原设备使用PX系列时,可以优先从PAG系列中寻找替换型号。但…

2026/7/24 0:03:10

jdk8 把list 扁平化成String 多个以逗号分隔

在 JDK 8 中&#xff0c;将 List 扁平化为以逗号分隔的 String&#xff0c;有几种非常简洁且高效的方法。&#x1f680; 推荐方案&#xff1a;使用 Collectors.joining()这是最标准的 Java 8 写法&#xff0c;适用于 List<String>。javaimport java.util.stream.Collecto…

2026/7/24 0:03:10

java 两个 long id 怎么合并成一个long id 并且不重复

“把两个 Long ID 合并成一个唯一的 Long ID&#xff0c;且保证不重复”这个需求&#xff0c;在 Java 里直接做数学上的“完美合并”是不可能的。因为两个 Long&#xff08;各 64 位&#xff09;要合并成一个 Long&#xff08;64 位&#xff09;&#xff0c;在信息论上是有损压…

2026/7/23 23:42:43

3个高效策略:快速掌握Axure中文界面配置

3个高效策略&#xff1a;快速掌握Axure中文界面配置 【免费下载链接】axure-cn Chinese language file for Axure RP. Axure RP 简体中文语言包。支持 Axure 11、10、9。不定期更新。 项目地址: https://gitcode.com/gh_mirrors/ax/axure-cn 还在为Axure RP的英文界面感…