news 2026/10/28 2:03:15

DDD(领域驱动设计)篇二:DDD 和三层架构到底差在哪?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
DDD(领域驱动设计)篇二:DDD 和三层架构到底差在哪?

DDD(领域驱动设计)篇一:DDD 到底在解决什么问题?

1、先摆结构:两个架构长什么样

你熟悉的三层架构,和 mpc 中台(我们系统里的中台叫mpc)实际的 DDD 分层,长这样:

左边是三层:所有活挤在 Service 一层里。右边是 DDD:同样一次请求被拆成四层,注意"业务规则"住在领域层,锁和缓存这类技术细节被隔离在最底下。

看起来只是把 Service 拆成了三块?不。关键区别不是层数,是"业务逻辑搬家了"。

打个比方:三层架构像一家什么都管的便利店,一个店员(Service 方法)同时收银、煮关东煮、盘库存、修打印机。生意小没事,生意大了每个店员都得是全能选手,换一个店员(改需求),另三样活都可能被牵连。

DDD 像一家正规餐厅:应用层是领班——不做饭,只安排哪桌的菜、交给哪个厨师;领域层是厨师和菜谱——真正的手艺在这里;基础设施层是后厨设备——冰箱坏了换设备,菜谱不用改。

2、拿真代码对比一下

三层版,patient 模块下单 OrderServiceImpl.java 的createOrder(一千六百行大类中的一员),简化后:

@Transactional(rollbackFor = Exception.class) public OrderSeqVO createOrder(CreateOrderDTO dto) { // 业务校验:患者是否满足挂号条件 boolean checkHospitalValid = checkHospitalValid(consultationValidDTO); Validator.validateTrue(checkHospitalValid, "就诊人信息不满足条件!"); // 远程调用:查医院特色服务,决定走哪条分支 Map<String, DictVo> specialServiceMap = hospitalApi .getHospitalSpecialServerList(dto.getHospitalId()).getData()...; // 业务分支:续方购药走这、特色服务走那、在线复诊还要校验号源 if (serverTypeEnum == ServerTypeEnum.DOCTOR_CONTINUE_BUY) { ... } // 技术细节:分布式锁防重复下单 RLock lock = redissonClient.getLock(SysContextHolder.getUserId() + "createOrder"); if (!lock.tryLock(1, 30, TimeUnit.SECONDS)) { GlobalException.throwEx("请勿重复提交订单"); } // 再往后:号源智能分配、耗时统计、按业务类型决定是否重试…… }

数一数,一个方法混了五种东西,而且变更节奏完全不同:

成分变更频率举例
业务规则月月改、经常改挂号条件、医生号源匹配、新的挂号类型
远程调用偶尔改hospitalApi 查特色服务
技术细节半年不动Redisson 锁、tryLock 参数
流程编排随业务改分支判断、重试
统计观测几乎不动StopWatch 耗时统计

痛点不在"代码烂"——每段单拎出来都没毛病。痛点在五种节奏不同的东西拧在一起,改任何一个都得通读全文。

DDD 版,同一工程里 mpc 中台的订单取消,OrderBaseSalesRuntimeContext.java(应用层,就是那个"领班"):

@Transactional(rollbackFor = Exception.class) public Result<CancelOrderDto> cancelOrder(CancelOrderParam param) { // 1. 把订单实体从仓库捞出来 Optional<OrderBasedSalesEntity> optional = orderEntityRepo.findOrder(orderId, orderNo, sourceChannel, externalOrderNo); OrderBasedSalesEntity orderEntity = optional.get(); // 2. 加锁交给锁的适配器,不自己摸 Redisson return iDomainLockAdapter.logicWithLock(orderEntity.getOrderLockKey(), () -> { // 3. 触发"取消"事件,规则由状态机和 Handler 说话 EventModel cancelEvent = EventModelFactory.INSTANCE .getEventModel(SalesOrderEventConstants.cancel); cancelEvent.setValueObject(JSONUtil.parseObj(param)); orderEntity.onEvent(cancelEvent); // ← 核心就这一行 return Result.success(new CancelOrderDto()); }); }

那"什么状态下能取消、取消后干啥"去哪了?——能不能取消由状态机描述符说了算,没注册的"状态+事件"组合框架直接抛异常;取消后干什么由 Handler 说了算;锁怎么加由 IDomainLockAdapter 说了算,领域层连 Redisson 的类都没 import 过。

3、逐项对比表

维度三层架构DDD
业务逻辑住在哪Service 方法里领域对象 + 状态机 + Handler
Entity 的角色数据袋子(贫血),只有 getter/setter有行为有规则(充血),如isOrderRefundable()
状态流转怎么管每个方法手写 if/setStatus状态机统一声明,非法流转自动拦截
改一条规则全局搜、多处改、容易漏通常只改一处
分布式锁/缓存直接 import Redisson/RedisTemplate适配器隔离,领域层不知道 Redis 存在
单元测试依赖一堆,mock 到手软领域规则不碰中间件,纯逻辑好测
上手成本低,应届生一周上手高,概念多,需要人带
简单接口的代价三个类搞定可能要建实体、描述符、Handler 一套

最后两行是 DDD 的真实代价,不吹不黑。

4、到底什么时候用哪个

判断逻辑其实就两问:有没有复杂的状态流转?规则是不是常变 / 多团队共用?决策路径是这样的:

对应的场景表:

场景建议
配置管理、字典、简单 CRUD三层,别折腾
复杂查询、报表、导出三层(查询为主就该轻快)
订单/支付/退款这类多状态流转DDD 收益明显
规则复杂且常变(计费、退费、风控)DDD
多团队共用的中台/平台DDD 划边界几乎是刚需

我们项目就是现成的印证:patient 模块面向 C 端单一业务,接口虽多但链路相对独立,三层够用,硬改 DDD 属于过度设计;mpc 中台是库存、订单、支付回调、IM 好几个业务方共用一套骨架,外部还有"京东极速咨询"这种多渠道接入(描述符里label = "jd_quickmed_service"),领域骨架让新业务接入变成"声明一个描述符 + 写几个 Handler"。

同一个工程、两种架构并存,这不是技术债,是按业务复杂度选型的正常状态。

5、一个容易踩的认知坑

很多人以为"DDD = 把 Service 的代码搬到 Entity 里"。不对。

搬家只是表象,前提是你先想清楚业务的边界和规则——哪些状态、哪些事件、哪些规则属于"订单"这个地盘。想不清楚就动手,结果只是把一锅粥从一个碗倒进另一个碗。这也是为什么 DDD 实践里"先和产品对语言、先画状态图"比"先写代码"重要得多。


6、思考题

翻出你上周写过的最复杂的一个 Service 方法(你自己心里有数是哪个),做三件事:

  1. 数一数里面混了几类职责:业务规则、远程调用、缓存、锁、事务、参数组装……

  2. 找出其中的"状态变更"代码:改了哪个字段?这个字段还有哪些别处也在改?

  3. 问自己:如果这个方法只保留业务规则,其他都挪走,它会变短多少?

带着答案进入下一篇——把项目里真实的"三层订单"和"DDD 订单"放在显微镜下对照。

DDD(领域驱动设计)篇三:实战对照——同一个订单业务,两种架构怎么写

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

Visual Studio 动态链接库(DLL)生成完全指南

一、前言 一个完整的 DLL 工程必须满足两个核心条件&#xff1a; 正确的项目类型配置&#xff1a;必须生成 .dll 文件&#xff0c;而不是 .exe。显式的符号导出&#xff1a;必须将需要公开的函数/类声明为导出&#xff0c;否则外部程序无法链接。 本文将从项目创建、代码编写…

作者头像 李华
网站建设 2026/10/28 2:02:34

产品经理的“产品感“到底是什么?一套可训练的判断框架

你一定听过这样的评价&#xff1a;“这个PM产品感不错。“但真要追问"产品感到底是什么”&#xff0c;大多数人只能含糊地说"就是感觉对”。 产品感不是玄学&#xff0c;而是一种可被拆解、可被训练的判断力。 本文把它拆成三个构成&#xff0c;并给出一套日常可落地…

作者头像 李华
网站建设 2026/10/28 2:02:23

aubo学习:realsense手眼标定

手眼标定到底在做什么&#xff1f;手眼标定需要计算相机与机械臂末端之间的固定空间关系&#xff1a;base_link&#xff1a;机械臂基座坐标系wrist3_Link&#xff1a;机械臂末端附近的坐标系camera_link&#xff1a;相机主体坐标系camera_color_optical_frame&#xff1a;相机彩…

作者头像 李华
网站建设 2026/10/28 2:01:55

305米网线一箱能布多少米?为什么实际施工长度往往更短?

305米网线为什么实际布线长度不足&#xff1f;从路径测量到施工余量计算 正文 一箱标称305米的网线&#xff0c;并不意味着现场一定能够完成305米有效布线。实际可完成的线路数量和总长度&#xff0c;取决于每条线路的实际路径、两端预留、施工绕行以及剩余线缆是否能够继续利用…

作者头像 李华
网站建设 2026/10/28 2:01:48

Agent从0开始学习

前言 本篇博客用于记录本人学习Agent的过程以及理解&#xff0c;如有错误&#xff0c;欢迎批评指正&#xff01; 目录 前言 一、Agent基础概念与运行机制 1.1 基础概念 1.2 运行机制 1.2.1 Agent与LLM单次调用的区别 1.2.2 自主决策能力 1.2.3 核心循环 补充 二、核心…

作者头像 李华