
第5课应用层与领域服务一、目标通过本课你将学会理解应用服务与领域服务的职责边界将复杂业务逻辑如折扣计算、库存检查提取为独立的领域服务重构OrderLifecycleService让其更“轻”二、概念回顾层级职责是否依赖领域模型示例应用层Application Service协调领域对象完成用例处理事务、权限、事件等✅ 是下单、支付、发货领域层Domain Service实现不适合放在实体或值对象中的业务规则❌ 否折扣计算、库存检查基础设施层Infrastructure负责外部资源访问数据库、API、MQ等✅ 是JPA、Redis、消息总线三、改造思路当前的OrderLifecycleServicepublicOrdercreateOrder(){OrderordernewOrder(OrderId.newId());order.addMenuItem(menu.findItem(Fried Rice),1);order.addMenuItem(menu.findItem(Coke),2);repository.save(order);returnorder;}问题业务逻辑选菜、折扣、校验都在应用层随着逻辑变复杂应用层容易臃肿Menu、Repository 等依赖太多容易造成循环依赖。我们要做的是把业务逻辑拆分为领域服务让OrderLifecycleService只负责“编排”。四、实现代码新建领域服务PricingService负责折扣计算、订单总额调整。packagecom.example.ordersystem.domain.order.service;importcom.example.ordersystem.domain.order.model.Money;importcom.example.ordersystem.domain.order.model.Order;importcom.example.ordersystem.domain.order.model.OrderItem;importorg.springframework.stereotype.Service;/** * 领域服务计算订单价格、折扣逻辑 */ServicepublicclassPricingService{/** * 根据业务规则计算订单总价 * 示例规则 * - 如果总额超过 50 元打 9 折 * - 如果包含 Coke第二瓶半价 */publicMoneycalculateTotal(Orderorder){doubletotal0;for(OrderItemitem:order.getItems()){doubleitemTotalitem.getPrice().getAmount()*item.getQuantity();// 特殊优惠Coke 第二瓶半价if(item.getName().equalsIgnoreCase(Coke)item.getQuantity()2){itemTotal-item.getPrice().getAmount()*0.5;}totalitemTotal;}// 满减折扣if(total50){total*0.9;}returnnewMoney(total);}}新建领域服务InventoryService负责库存校验通常会依赖外部仓储系统。packagecom.example.ordersystem.domain.order.service;importcom.example.ordersystem.domain.menu.model.MenuItem;importorg.springframework.stereotype.Service;/** * 领域服务库存检查 * 这里简单模拟逻辑真实系统可能连接库存微服务 */ServicepublicclassInventoryService{publicbooleancheckStock(MenuItemitem,intquantity){// 模拟可乐最多卖 100 瓶炒饭最多卖 50 份if(item.getName().equalsIgnoreCase(Coke)){returnquantity100;}if(item.getName().equalsIgnoreCase(Fried Rice)){returnquantity50;}returntrue;}}重构应用层OrderLifecycleServicepackagecom.example.ordersystem.application.order;importcom.example.ordersystem.domain.menu.model.Menu;importcom.example.ordersystem.domain.menu.model.MenuItem;importcom.example.ordersystem.domain.order.model.*;importcom.example.ordersystem.domain.order.repository.OrderRepository;importcom.example.ordersystem.domain.order.service.InventoryService;importcom.example.ordersystem.domain.order.service.PricingService;importorg.springframework.stereotype.Service;importorg.springframework.transaction.annotation.Transactional;/** * 应用服务编排订单生命周期 * 仅协调各领域对象、服务不承载具体业务逻辑 */ServicepublicclassOrderLifecycleService{privatefinalOrderRepositoryrepository;privatefinalMenumenu;privatefinalPricingServicepricingService;privatefinalInventoryServiceinventoryService;publicOrderLifecycleService(OrderRepositoryrepository,Menumenu,PricingServicepricingService,InventoryServiceinventoryService){this.repositoryrepository;this.menumenu;this.pricingServicepricingService;this.inventoryServiceinventoryService;}/** 创建订单 */TransactionalpublicOrdercreateOrder(){OrderordernewOrder(OrderId.newId());MenuItemfriedRicemenu.findItem(Fried Rice);MenuItemcokemenu.findItem(Coke);if(!inventoryService.checkStock(friedRice,1))thrownewIllegalStateException(Fried Rice 库存不足);if(!inventoryService.checkStock(coke,2))thrownewIllegalStateException(Coke 库存不足);order.addMenuItem(friedRice,1);order.addMenuItem(coke,2);// 计算总价含优惠MoneytotalpricingService.calculateTotal(order);System.out.println(订单优惠后总价total.getAmount());repository.save(order);returnorder;}/** 支付订单 */TransactionalpublicOrderpayOrder(StringorderId){Orderorderrepository.findById(newOrderId(orderId));order.pay();repository.save(order);returnorder;}/** 确认订单 */TransactionalpublicOrderconfirmOrder(StringorderId){Orderorderrepository.findById(newOrderId(orderId));order.confirm();repository.save(order);returnorder;}}五、运行与验证启动后执行POST http://localhost:8080/orders/create控制台输出订单优惠后总价18.5API 响应{id:{value:0ff7d...},status:CREATED,totalAmount:{amount:18.5}}说明PricingService正常计算InventoryService校验通过无循环依赖各层职责清晰。六、总结角色职责位置OrderLifecycleService用例编排事务、调用应用层PricingService折扣逻辑领域层InventoryService业务校验库存领域层JpaOrderRepositoryImpl数据持久化基础设施层