当前位置: 首页 > 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.jsqmd.com/news/951945/

相关文章:

  • 2026玉溪市权威认证贵金属回收 TOP5+黄金回收白银回收铂金回收门店地址电话推荐
  • MySQL5.7 数据库安装、初始化、密码修改、远程连接完整实战
  • 新手零基础入门claude desktop:利用快马平台生成交互式学习项目
  • 别被KEIL的语法检查骗了!深入理解‘error in include chain’警告与编译器真实行为的差异
  • 3分钟免费安装AI象棋教练:Vin象棋让棋艺提升变得简单快速
  • 【国家级信创认证】:首套通过上交所智能审核适配测试的AI上市辅助平台,内测资格最后47席
  • 3分钟掌握终极窗口控制术:免费开源工具让你完全掌控Windows窗口大小
  • 别再手动导入了!用BurpSuite CLI和Docker实现自动化测试环境搭建与数据恢复
  • 别再乱设max-http-header-size了!SpringBoot内嵌Tomcat的这几个Connector参数详解与避坑指南
  • 星穹铁道自动化助手:三月七小助手完整使用指南
  • 2026苏州配眼镜推荐:干将东路写字楼里的光学革新与五类方案解析 - 配眼镜新资讯
  • 做课件找不到合适BGM?11个优质课件背景音乐站点整理
  • 2026年企业破产重整律师事务所服务解析:炜衡密云分所核心优势解读 - 商业科技观察
  • Labview视觉开发环境搭建保姆级教程(含VDM/VAS安装避坑指南)
  • 让音乐看得见:用Lano Visualizer打造动态桌面音频可视化体验
  • 怒怼微软后,研究员公开GitHub高危漏洞:一个链接拿下私有仓库权限
  • 从汽车悬架到手机防抖:阻尼振动微分方程在工程中的实际应用盘点
  • 告别JSON对比的烦恼:这个可视化工具如何帮你节省90%调试时间
  • 实战集成:利用快马ai实现cad安装与项目管理系统的自动化对接
  • 【状态估计】电力系统状态估计中的异常检测与分类附Matlab代码
  • Eledoisin-Related Peptide;KFIGLM
  • 苏州配眼镜推荐:2026五类需求适配方案解析攻略 - 配眼镜新资讯
  • 2026年当下江苏省纳米釉面漆实力厂家怎么选?深度解析技术壁垒与市场适配逻辑 - 2026年企业资讯
  • 深度解析开源PDF编辑器PDF4QT:从基础操作到高级功能的完整实战指南
  • SAP顾问转型记:当GUI事务码FI12失效,我是如何用Fiori App搞定银行账户管理的
  • 083、无人机航拍小目标检测:VisDrone 数据集上的 YOLO 专项优化实战
  • 别再手动加载数据了!用Simulink Model Properties的回调函数自动搞定(附set_param命令详解)
  • 别只当黑盒用!深入.pyd文件:用dir、help和inspect模块探索其内部接口
  • Ecdysis-Triggering Hormone (Manduca sexta) (Mas-ETH)
  • Forza Mods AIO:终极免费修改工具,彻底释放《极限竞速》游戏潜能 [特殊字符]