DDD学习第2课建立领域模型

发布时间:2026/7/15 8:14:50

DDD学习第2课建立领域模型 第2课建立领域模型Entity、Value Object目标从业务出发完善领域模型加入订单状态、业务规则强化“领域逻辑”在实体内部完成而非应用层业务分析订餐流程用户创建订单初始状态CREATED用户支付后状态变为PAID餐厅确认并准备状态变为CONFIRMED食物送达后状态变为COMPLETED若取消则状态变为CANCELLED我们要解决的问题状态转移规则不能从CREATED直接跳到COMPLETED封装金额运算、状态检查逻辑在领域对象内部应用层只负责“用例编排”不写业务细节修改/新增的类一览类名职责OrderStatus表示订单状态值对象Order聚合根负责状态流转逻辑Money封装金额加减法OrderItem值对象代表一个菜品项DomainException业务异常类代码实现OrderStatus.javapackagecom.example.ordersystem.domain.order.model;/** * 订单状态值对象 * 定义订单生命周期中的所有可能状态。 */publicenumOrderStatus{CREATED,// 已创建PAID,// 已支付CONFIRMED,// 已确认COMPLETED,// 已完成CANCELLED;// 已取消}DomainException.javapackagecom.example.ordersystem.domain.shared;/** * 自定义领域异常用于在业务规则违反时抛出。 */publicclassDomainExceptionextendsRuntimeException{publicDomainException(Stringmessage){super(message);}}修改Order.java— 加入状态与业务逻辑packagecom.example.ordersystem.domain.order.model;importcom.example.ordersystem.domain.shared.DomainException;importjava.util.ArrayList;importjava.util.List;/** * 聚合根Order * 封装订单的状态变化、金额计算等核心业务逻辑。 */publicclassOrder{privatefinalOrderIdid;privatefinalListOrderItemitemsnewArrayList();privateMoneytotalAmountMoney.zero();privateOrderStatusstatusOrderStatus.CREATED;// 初始状态publicOrder(OrderIdid){this.idid;}/** 添加菜品项 */publicvoidaddItem(Stringname,intquantity,doubleprice){if(status!OrderStatus.CREATED){thrownewDomainException(只有在订单创建阶段才能添加菜品。);}OrderItemitemnewOrderItem(name,quantity,newMoney(price));items.add(item);totalAmounttotalAmount.add(item.subtotal());}/** 支付订单 */publicvoidpay(){if(status!OrderStatus.CREATED){thrownewDomainException(只有在创建状态的订单才能支付。);}this.statusOrderStatus.PAID;}/** 餐厅确认订单 */publicvoidconfirm(){if(status!OrderStatus.PAID){thrownewDomainException(订单必须先支付才能确认。);}this.statusOrderStatus.CONFIRMED;}/** 完成订单 */publicvoidcomplete(){if(status!OrderStatus.CONFIRMED){thrownewDomainException(订单未确认不能完成。);}this.statusOrderStatus.COMPLETED;}/** 取消订单 */publicvoidcancel(){if(statusOrderStatus.COMPLETED){thrownewDomainException(已完成的订单不能取消。);}this.statusOrderStatus.CANCELLED;}// GetterspublicOrderIdgetId(){returnid;}publicMoneygetTotalAmount(){returntotalAmount;}publicListOrderItemgetItems(){returnitems;}publicOrderStatusgetStatus(){returnstatus;}}修改OrderItem.javapackagecom.example.ordersystem.domain.order.model;/** * 值对象OrderItem * 不单独存在只依附于 Order。 * 没有独立标识无ID。 */publicclassOrderItem{publicfinalStringname;publicfinalintquantity;publicfinalMoneyprice;publicOrderItem(Stringname,intquantity,Moneyprice){if(quantity0){thrownewIllegalArgumentException(数量必须大于0。);}this.namename;this.quantityquantity;this.priceprice;}/** 返回单项总价 */publicMoneysubtotal(){returnprice.multiply(quantity);}publicStringgetName(){returnname;}publicintgetQuantity(){returnquantity;}publicMoneygetPrice(){returnprice;}}修改Money.javapackagecom.example.ordersystem.domain.order.model;/** * 值对象Money * 封装金额运算保证精度安全此处简单实现。 */publicclassMoney{privatefinaldoubleamount;publicMoney(doubleamount){if(amount0){thrownewIllegalArgumentException(金额不能为负数。);}this.amountamount;}publicstaticMoneyzero(){returnnewMoney(0);}publicMoneyadd(Moneyother){returnnewMoney(this.amountother.amount);}publicMoneymultiply(intfactor){returnnewMoney(this.amount*factor);}publicdoublegetAmount(){returnamount;}OverridepublicStringtoString(){returnString.format(%.2f,amount);}}新的应用服务OrderLifecycleService.java我们新增一个应用服务来演示订单的完整生命周期。packagecom.example.ordersystem.application.order;importcom.example.ordersystem.domain.order.model.Order;importcom.example.ordersystem.domain.order.model.OrderId;importcom.example.ordersystem.domain.order.repository.OrderRepository;importorg.springframework.stereotype.Service;/** * 应用层服务编排订单生命周期的操作 * 注意不包含任何业务逻辑只调用领域对象的方法。 */ServicepublicclassOrderLifecycleService{privatefinalOrderRepositoryrepository;publicOrderLifecycleService(OrderRepositoryrepository){this.repositoryrepository;}publicOrdercreateOrder(){OrderordernewOrder(OrderId.newId());order.addItem(Fried Rice,1,12.5);order.addItem(Coke,2,3.0);repository.save(order);returnorder;}publicOrderpayOrder(Stringid){Orderorderrepository.findById(newOrderId(id));order.pay();repository.save(order);returnorder;}publicOrderconfirmOrder(Stringid){Orderorderrepository.findById(newOrderId(id));order.confirm();repository.save(order);returnorder;}publicOrdercompleteOrder(Stringid){Orderorderrepository.findById(newOrderId(id));order.complete();repository.save(order);returnorder;}publicOrdercancelOrder(Stringid){Orderorderrepository.findById(newOrderId(id));order.cancel();repository.save(order);returnorder;}}更新 Controllerpackagecom.example.ordersystem.interfaces.api;importcom.example.ordersystem.application.order.OrderLifecycleService;importcom.example.ordersystem.domain.order.model.Order;importorg.springframework.web.bind.annotation.*;/** * 控制层仅负责暴露 HTTP API * 不包含业务逻辑。 */RestControllerRequestMapping(/orders)publicclassOrderController{privatefinalOrderLifecycleServiceservice;publicOrderController(OrderLifecycleServiceservice){this.serviceservice;}PostMapping(/create)publicOrdercreate(){returnservice.createOrder();}PostMapping(/{id}/pay)publicOrderpay(PathVariableStringid){returnservice.payOrder(id);}PostMapping(/{id}/confirm)publicOrderconfirm(PathVariableStringid){returnservice.confirmOrder(id);}PostMapping(/{id}/complete)publicOrdercomplete(PathVariableStringid){returnservice.completeOrder(id);}PostMapping(/{id}/cancel)publicOrdercancel(PathVariableStringid){returnservice.cancelOrder(id);}}测试一下生命周期示例创建订单POST http://localhost:8080/orders/create→ 返回订单 JSON状态CREATED支付订单POST http://localhost:8080/orders/{id}/pay→ 返回订单 JSON状态PAID确认订单POST http://localhost:8080/orders/{id}/confirm→ 返回订单 JSON状态CONFIRMED完成订单POST http://localhost:8080/orders/{id}/complete→ 返回订单 JSON状态COMPLETED第2课总结你现在学会了用值对象表达不可变的业务概念Money、OrderStatus用实体Entity表达可变的业务对象Order、OrderItem在聚合根 Order内部维护业务规则状态流转应用层只负责流程编排

相关新闻