当前位置: 首页 > news >正文

DDD-016:分层架构与 DDD

DDD-016:分层架构与 DDD

16.1 分层架构概述

16.1.1 什么是分层架构?

【原理】

分层架构(Layered Architecture)是软件设计中最经典的架构模式,其核心思想是将系统按职责划分为多个水平层次,每层只依赖于其下层,实现关注点分离。

分层架构的基本原则:

  • 单向依赖:上层依赖下层,下层不依赖上层
  • 职责单一:每层只负责一类特定的功能
  • 接口隔离:层与层之间通过定义良好的接口通信
  • 可替换性:每层可以独立替换实现

16.1.2 传统分层架构

【历史架构问题】

传统 Java EE 和早期 Spring 项目通常采用三层架构:

┌─────────────────────────────────────┐ │ 表现层(Web Layer) │ ← Controller、JSP、HTML ├─────────────────────────────────────┤ │ 业务逻辑层(Service Layer) │ ← Service、业务逻辑 ├─────────────────────────────────────┤ │ 数据访问层(DAO Layer) │ ← DAO、Repository、SQL └─────────────────────────────────────┘

传统三层架构的代码示例:

// ❌ 传统三层架构的典型实现// ========== 表现层 ==========@RestController@RequestMapping("/orders")publicclassOrderController{@AutowiredprivateOrderServiceorderService;@PostMappingpublicResultcreateOrder(@RequestBodyCreateOrderRequestrequest){LongorderId=orderService.createOrder(request.getUserId(),request.getProductIds(),request.getCouponCode());returnResult.success(orderId);}}// ========== 业务逻辑层 ==========@ServicepublicclassOrderService{@AutowiredprivateOrderDaoorderDao;@AutowiredprivateProductDaoproductDao;@AutowiredprivateUserDaouserDao;@AutowiredprivateCouponDaocouponDao;@TransactionalpublicLongcreateOrder(LonguserId,List<Long>productIds,StringcouponCode){// 问题1:业务逻辑直接操作多个数据表Useruser=userDao.findById(userId);List<Product>products=productDao.findByIds(productIds);Couponcoupon=couponDao.findByCode(couponCode);// 问题2:业务规则散落在Service中if(user==null){thrownewBusinessException("用户不存在");}if(products.isEmpty()){thrownewBusinessException("商品不能为空");}// 问题3:领域对象贫血,只有getter/setterOrderorder=newOrder();order.setUserId(userId);order.setStatus("CREATED");order.setCreateTime(newDate());BigDecimaltotalAmount=BigDecimal.ZERO;for(Productproduct:products){totalAmount=totalAmount.add(product.getPrice());}// 问题4:复杂的业务计算在Service中if(coupon!=null&&coupon.isValid()){totalAmount=totalAmount.subtract(coupon.getAmount());}order.setTotalAmount(totalAmount);orderDao.insert(order);// 问题5:直接操作其他表for(Productproduct:products){OrderItemitem=newOrderItem();item.setOrderId(order.getId());item.setProductId(product.getId());item.setPrice(product.getPrice());orderItemDao.insert(item);}returnorder.getId();}}// ========== 数据访问层 ==========@RepositorypublicclassOrderDao{@AutowiredprivateJdbcTemplatejdbcTemplate;publicvoidinsert(Orderorder){jdbcTemplate.update("INSERT INTO orders (user_id, status, total_amount, create_time) VALUES (?, ?, ?, ?)",order.getUserId(),order.getStatus(),order.getTotalAmount(),order.getCreateTime());}publicOrderfindById(Longid){returnjdbcTemplate.queryForObject("SELECT * FROM orders WHERE id = ?",this::mapRow,id);}}// ========== 贫血领域对象 ==========publicclassOrder{privateLongid;privateLonguserId;privateStringstatus;privateBigDecimaltotalAmount;privateDatecreateTime;// 只有getter/setter,没有业务行为publicLonggetId(){returnid;}publicvoidsetId(Longid){this.id=id;}publicLonggetUserId(){returnuserId;}publicvoidsetUserId(LonguserId){this.userId=userId;}// ... 其他getter/setter}

16.1.3 传统分层架构的问题

【根本原因分析】

问题具体表现根本原因
贫血领域模型实体只有getter/setter,没有业务行为面向过程思维,把领域对象当数据容器
Service层臃肿一个Service类上千行,包含各种业务逻辑业务逻辑没有归属到领域对象
业务逻辑泄漏Controller也包含验证逻辑,Service也操作多个表层次职责不清晰
难以测试测试业务逻辑需要启动整个应用上下文领域逻辑与基础设施强耦合
难以扩展修改一个业务规则影响多处代码缺乏内聚,修改发散
数据库依赖所有逻辑都依赖数据库才能运行没有领域层,数据驱动而非领域驱动

问题示意图:

┌─────────────────────────────────────────────────────────┐ │ OrderController │ │ ┌─────────────────────────────────────────────────┐ │ │ │ ❌ 包含:参数验证、异常处理、业务逻辑判断 │ │ │ └─────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────┐ │ OrderService │ │ ┌─────────────────────────────────────────────────┐ │ │ │ ❌ 包含: │ │ │ │ - 用户验证逻辑 │ │ │ │ - 商品验证逻辑 │ │ │ │ - 价格计算逻辑 │ │ │ │ - 优惠券逻辑 │ │ │ │ - 库存扣减逻辑 │ │ │ │ - 积分逻辑 │ │ │ │ - ...各种不属于此处的逻辑 │ │ │ └─────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────┐ │ OrderDao / ProductDao / UserDao / CouponDao │ │ ┌─────────────────────────────────────────────────┐ │ │ │ ✅ 数据访问,但无法阻止Service直接操作多表 │ │ │ └─────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────┐ │ Order(贫血实体) │ │ ┌─────────────────────────────────────────────────┐ │ │ │ ❌ 只有getter/setter,业务逻辑全在Service │ │ │ └─────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────┘

16.2 DDD 的分层架构

16.2.1 DDD 四层架构

【DDD 如何解决】

DDD 在传统三层架构的基础上,引入了领域层(Domain Layer),将业务逻辑从Service层剥离,形成四层架构:

┌─────────────────────────────────────────────────────────┐ │ 用户界面层(User Interface Layer) │ │ Controller、DTO、Assembler、Facade │ └─────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────┐ │ 应用层(Application Layer) │ │ Application Service、事务管理 │ └─────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────┐ │ 领域层(Domain Layer) │ │ Entity、Value Object、Domain Service、Repository │ └─────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────┐ │ 基础设施层(Infrastructure Layer) │ │ Repository实现、消息队列、外部服务调用、缓存 │ └─────────────────────────────────────────────────────────┘

16.2.2 各层职责详解

用户界面层(User Interface Layer)

职责:处理用户请求,展示数据,不包含业务逻辑。

用户界面层包含: ┌─────────────────────────────────────────────────────────┐ │ Controller - 接收HTTP请求,路由分发 │ │ DTO - 数据传输对象,与前端交互的数据结构 │ │ Assembler - DTO与领域对象的转换 │ │ Facade - 简化前端调用的门面服务 │ │ Validator - 请求参数格式验证(非业务验证) │ └─────────────────────────────────────────────────────────┘ ✅ 应该做: - 接收请求、解析参数 - 调用应用服务 - 将结果转换为DTO返回 - HTTP相关的错误处理(404、401等) ❌ 不应该做: - 业务逻辑判断 - 直接调用Repository - 直接操作领域对象

代码示例:

// ✅ 正确的Controller实现@RestController@RequestMapping("/orders")publicclassOrderController{privatefinalOrderApplicationServiceorderApplicationService;publicOrderController(OrderApplicationServiceorderApplicationService){this.orderApplicationService=orderApplicationService;}@PostMappingpublicApiResponse<OrderResponse>createOrder(@RequestBody@ValidCreateOrderRequestrequest){// 1. DTO转换CreateOrderCommandcommand=CreateOrderCommand.builder().userId(request.getUserId()).productItems(request.getProductItems()).couponCode(request.getCouponCode()).build();// 2. 调用应用服务(注意:不包含业务逻辑)LongorderId=orderApplicationService.createOrder(command);// 3. 返回结果returnApiResponse.success(newOrderResponse(orderId));}@GetMapping("/{orderId}")publicApiResponse<OrderDetailResponse>getOrder(@PathVariableLongorderId){OrderDetailDTOdto=orderApplicationService.getOrderDetail(orderId);returnApiResponse.success(dto);}}// DTO定义@DatapublicclassCreateOrderRequest{@NotNull(message="用户ID不能为空")privateLonguserId;@NotEmpty(message="商品列表不能为空")privateList<ProductItem>productItems;privateStringcouponCode;@DatapublicstaticclassProductItem{@NotNullprivateLongproductId;@NotNull@Min(1)privateIntegerquantity;}}
应用层(Application Layer)

职责:协调领域对象完成任务,管理事务,不包含业务规则。

应用层包含: ┌─────────────────────────────────────────────────────────┐ │ Application Service - 应用服务,协调用例执行 │ │ Command/Query - 命令/查询对象 │ │ 事务管理 - 确保用例的事务完整性 │ │ 安全检查 - 权限验证(可委托) │ └─────────────────────────────────────────────────────────┘ ✅ 应该做: - 协调领域对象完成业务用例 - 开启和提交事务 - 调用基础设施服务 - 发布领域事件 - DTO与领域对象转换 ❌ 不应该做: - 业务规则判断(应在领域层) - 直接SQL操作(应通过Repository) - 复杂计算逻辑(应在领域对象中)

代码示例:

// ✅ 正确的应用服务实现@ServicepublicclassOrderApplicationService{privatefinalOrderRepositoryorderRepository;privatefinalProductRepositoryproductRepository;privatefinalCouponRepositorycouponRepository;privatefinalEventPublishereventPublisher;publicOrderApplicationService(OrderRepositoryorderRepository,ProductRepositoryproductRepository,CouponRepositorycouponRepository,EventPublishereventPublisher){this.orderRepository=orderRepository;this.productRepository=productRepository;this.couponRepository=couponRepository;this.eventPublisher=eventPublisher;}@TransactionalpublicLongcreateOrder(CreateOrderCommandcommand){// 1. 准备数据(不包含业务逻辑)List<Product>products=productRepository.findByIds(command.getProductItems().stream().map(ProductItem::getProductId).collect(Collectors.toList()));Map<Long,Integer>quantities=command.getProductItems().stream().collect(Collectors.toMap(ProductItem::getProductId,ProductItem::getQuantity));// 2. 创建领域对象(业务逻辑在Order内部)Orderorder=Order.create(command.getUserId(),products,quantities,command.getCouponCode());// 3. 应用优惠券(如果有)if(command.getCouponCode()!=null){Couponcoupon=couponRepository.findByCode(command.getCouponCode());if(coupon!=null){order.applyCoupon(coupon);}}// 4. 持久化orderRepository.save(order);// 5. 发布事件eventPublisher.publish(newOrderCreatedEvent(order.getId()));returnorder.getId();}@TransactionalpublicvoidcancelOrder(LongorderId,Stringreason){Orderorder=orderRepository.findById(orderId).orElseThrow(()->newOrderNotFoundException(orderId));// 业务逻辑在Order内部order.cancel(reason);orderRepository.save(order);eventPublisher.publish(newOrderCancelledEvent(orderId,reason));}@Transactional(readOnly=true)publicOrderDetailDTOgetOrderDetail(LongorderId){Orderorder=orderRepository.findById(orderId).orElseThrow(()->newOrderNotFoundException(orderId));returnOrderDetailDTO.from(order);}}
领域层(Domain Layer)

职责:核心业务逻辑,是系统的灵魂。

领域层包含: ┌─────────────────────────────────────────────────────────┐ │ Entity - 实体,有唯一标识 │ │ Value Object - 值对象,无标识,不可变 │ │ Aggregate - 聚合,一致性边界 │ │ Domain Service - 领域服务,跨对象操作 │ │ Repository接口 - 仓储接口(实现在基础设施层) │ │ Domain Event - 领域事件 │ │ Factory - 工厂,复杂对象创建 │ └─────────────────────────────────────────────────────────┘ ✅ 应该做: - 封装业务规则和约束 - 保证业务不变性(Invariants) - 定义业务行为和状态转换 - 领域对象之间的关系 ✅ 特点: - 不依赖任何框架(纯POJO) - 不依赖基础设施 - 高度可测试 - 高度可复用

代码示例:

// ✅ 充血的领域对象publicclassOrder{privateOrderIdid;
http://www.zskr.cn/news/1463562.html

相关文章:

  • 2026玉溪市权威认证贵金属回收 TOP5+黄金回收白银回收铂金回收门店地址电话推荐
  • 做课件找不到合适BGM?11个优质课件背景音乐站点整理
  • 2026苏州配眼镜推荐:干将东路写字楼里的光学革新与五类方案解析 - 配眼镜新资讯
  • 从汽车悬架到手机防抖:阻尼振动微分方程在工程中的实际应用盘点
  • 怒怼微软后,研究员公开GitHub高危漏洞:一个链接拿下私有仓库权限
  • SAP顾问转型记:当GUI事务码FI12失效,我是如何用Fiori App搞定银行账户管理的
  • 083、无人机航拍小目标检测:VisDrone 数据集上的 YOLO 专项优化实战
  • 别再手动加载数据了!用Simulink Model Properties的回调函数自动搞定(附set_param命令详解)
  • 别只当黑盒用!深入.pyd文件:用dir、help和inspect模块探索其内部接口
  • 005、Zephyr RTOS社区与生态介绍
  • 告别手动fuzz:用快马ai为burpsuite生成自动化漏洞检测脚本
  • GPT-4o实战指南:构建生产级编程智能体与数据分析工作流
  • 【教育AI合规落地白皮书】:教育部新规下AI工具嵌入课堂的4道安全红线与3级审计验证流程
  • 【头部金融机构AI认证实战白皮书】:97天完成NIST AI RMF与ISC² CC certification双轨整合
  • 从PEM到JKS:手把手教你将K8s TLS证书配置到Hadoop/Spring Boot Java应用
  • AI工具如何3天重构薪酬体系:从数据孤岛到实时动态调薪的12步落地清单
  • 扫地机器人地图边缘有毛刺?用OpenCV C++写个脚本一键美化(附完整代码)
  • Halcon区域处理三剑客:region_to_bin、label、mean到底怎么选?附完整代码示例
  • AntiDupl.NET图片去重终极指南:快速清理重复图片的完整教程
  • 效率提升:用快马AI自动化工具快速处理付款未获批准事项
  • COM3D2终极实时编辑器:5分钟掌握游戏角色属性修改技巧
  • DankDroneDownloader:无人机固件自由与历史版本恢复的终极解决方案
  • 三分钟破解Axure语言障碍:中文界面本地化实战方案
  • 五步构建完美黑苹果系统:OpenCore引导配置完全指南
  • 融资超500亿!DeepSeek估值逼近600亿美元,腾讯宁德时代争相入局
  • 2026年中央空调清洗公司推荐哪些?商业楼宇空调系统清洗选型指南 - 华旭传媒
  • SourceGit:让Git版本控制变得直观高效的跨平台图形化解决方案
  • [特殊字符] 拼多多大厂笔试题——正则表达式
  • 【深度解析】Gemma 4 12B:面向本地 Agent 工作流的统一多模态模型与 OpenAI 兼容接入实践
  • 【会议征稿通知 | 中国教育发展战略学会教育大数据专业委员会主办 | SPIE出版 | EI 、Scopus稳定检索】第六届先进算法与信号、图像处理国际学术会议(AASIP 2026)