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

DDD 领域驱动设计实战:从理论到代码

DDD 领域驱动设计实战:从理论到代码

别叫我大神,叫我 Alex 就好。DDD 不是银弹,但它是处理复杂业务逻辑的利器。

一、DDD 核心概念

1.1 分层架构

┌─────────────────────────────────────────┐ │ User Interface │ │ - Controller / DTO / Validator │ └──────────────┬──────────────────────────┘ │ ┌──────────────▼──────────────────────────┐ │ Application │ │ - Service / Use Case / Event │ └──────────────┬──────────────────────────┘ │ ┌──────────────▼──────────────────────────┐ │ Domain │ │ - Entity / Value Object / Aggregate │ │ - Domain Service / Repository │ └──────────────┬──────────────────────────┘ │ ┌──────────────▼──────────────────────────┐ │ Infrastructure │ │ - Persistence / Message / External │ └─────────────────────────────────────────┘

1.2 项目结构

com.example.order ├── application │ ├── dto │ │ ├── OrderDTO.java │ │ └── OrderItemDTO.java │ ├── event │ │ └── OrderEventPublisher.java │ ├── service │ │ └── OrderApplicationService.java │ └── mapper │ └── OrderMapper.java ├── domain │ ├── model │ │ ├── entity │ │ │ ├── Order.java │ │ │ └── OrderItem.java │ │ ├── valueobject │ │ │ ├── Address.java │ │ │ ├── Money.java │ │ │ └── OrderStatus.java │ │ └── aggregate │ │ └── OrderAggregate.java │ ├── service │ │ └── OrderDomainService.java │ ├── repository │ │ └── OrderRepository.java │ └── event │ ├── OrderCreatedEvent.java │ └── OrderPaidEvent.java ├── infrastructure │ ├── persistence │ │ ├── JpaOrderRepository.java │ │ └── entity │ │ ├── OrderPO.java │ │ └── OrderItemPO.java │ └── messaging │ └── KafkaEventPublisher.java └── interfaces ├── rest │ └── OrderController.java └── mq └── OrderEventListener.java

二、领域模型实现

2.1 实体与值对象

// 值对象 - 地址 @Value public class Address { private final String province; private final String city; private final String district; private final String detail; private final String zipCode; public Address(String province, String city, String district, String detail, String zipCode) { // 校验逻辑 if (StringUtils.isBlank(province)) { throw new IllegalArgumentException("Province cannot be empty"); } this.province = province; this.city = city; this.district = district; this.detail = detail; this.zipCode = zipCode; } // 值对象比较基于内容 @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Address address = (Address) o; return Objects.equals(province, address.province) && Objects.equals(city, address.city) && Objects.equals(district, address.district) && Objects.equals(detail, address.detail); } @Override public int hashCode() { return Objects.hash(province, city, district, detail); } } // 值对象 - 金额 @Value public class Money { private final BigDecimal amount; private final Currency currency; public Money(BigDecimal amount, Currency currency) { if (amount.compareTo(BigDecimal.ZERO) < 0) { throw new IllegalArgumentException("Amount cannot be negative"); } this.amount = amount; this.currency = currency; } public Money add(Money other) { if (this.currency != other.currency) { throw new IllegalArgumentException("Cannot add different currencies"); } return new Money(this.amount.add(other.amount), this.currency); } public Money multiply(int factor) { return new Money(this.amount.multiply(BigDecimal.valueOf(factor)), this.currency); } } // 实体 - 订单 @Entity public class Order extends BaseAggregateRoot<OrderId> { private OrderId id; private CustomerId customerId; private List<OrderItem> items; private Money totalAmount; private Address shippingAddress; private OrderStatus status; private LocalDateTime createTime; // 工厂方法 public static Order create(CustomerId customerId, List<OrderItem> items, Address shippingAddress) { Order order = new Order(); order.id = OrderId.generate(); order.customerId = customerId; order.items = new ArrayList<>(items); order.shippingAddress = shippingAddress; order.status = OrderStatus.CREATED; order.createTime = LocalDateTime.now(); order.calculateTotalAmount(); // 发布领域事件 order.registerEvent(new OrderCreatedEvent(order.id, order.totalAmount)); return order; } // 领域行为 public void pay(Payment payment) { if (status != OrderStatus.CREATED) { throw new IllegalStateException("Order can only be paid when created"); } if (!payment.getAmount().equals(this.totalAmount)) { throw new IllegalArgumentException("Payment amount does not match"); } this.status = OrderStatus.PAID; this.registerEvent(new OrderPaidEvent(this.id, payment.getAmount())); } public void ship(TrackingNumber trackingNumber) { if (status != OrderStatus.PAID) { throw new IllegalStateException("Order must be paid before shipping"); } this.status = OrderStatus.SHIPPED; this.registerEvent(new OrderShippedEvent(this.id, trackingNumber)); } public void cancel() { if (status == OrderStatus.SHIPPED || status == OrderStatus.DELIVERED) { throw new IllegalStateException("Cannot cancel shipped or delivered order"); } this.status = OrderStatus.CANCELLED; this.registerEvent(new OrderCancelledEvent(this.id)); } private void calculateTotalAmount() { this.totalAmount = items.stream() .map(OrderItem::getSubtotal) .reduce(Money.ZERO, Money::add); } // 业务规则校验 public boolean canBeModified() { return status == OrderStatus.CREATED; } }

2.2 聚合根

// 聚合根 public class Order extends BaseAggregateRoot<OrderId> { // 聚合内部实体 private List<OrderItem> items = new ArrayList<>(); // 聚合内部值对象 private ShippingAddress shippingAddress; private PaymentInfo paymentInfo; // 只能通过聚合根修改内部实体 public void addItem(Product product, int quantity, Money price) { // 业务规则 if (items.size() >= 20) { throw new IllegalArgumentException("Order cannot have more than 20 items"); } OrderItem item = new OrderItem(product.getId(), quantity, price); items.add(item); recalculateTotal(); } public void removeItem(int itemIndex) { if (itemIndex < 0 || itemIndex >= items.size()) { throw new IllegalArgumentException("Invalid item index"); } items.remove(itemIndex); recalculateTotal(); } // 保证聚合不变量 private void recalculateTotal() { this.totalAmount = items.stream() .map(item -> item.getPrice().multiply(item.getQuantity())) .reduce(Money.ZERO, Money::add); } } // 聚合根仓储接口 public interface OrderRepository { Order findById(OrderId id); void save(Order order); List<Order> findByCustomerId(CustomerId customerId); }

三、领域服务

// 领域服务 - 处理跨聚合的业务逻辑 @Service public class OrderDomainService { @Autowired private InventoryService inventoryService; @Autowired private PricingService pricingService; // 创建订单的复杂业务逻辑 public Order createOrder(CustomerId customerId, List<OrderItemRequest> itemRequests) { // 检查库存 for (OrderItemRequest request : itemRequests) { if (!inventoryService.isAvailable(request.getProductId(), request.getQuantity())) { throw new InsufficientStockException(request.getProductId()); } } // 计算价格 List<OrderItem> items = itemRequests.stream() .map(request -> { Money price = pricingService.calculatePrice( request.getProductId(), request.getQuantity() ); return new OrderItem(request.getProductId(), request.getQuantity(), price); }) .collect(Collectors.toList()); // 创建订单 Order order = Order.create(customerId, items); // 预留库存 for (OrderItem item : items) { inventoryService.reserve(item.getProductId(), item.getQuantity()); } return order; } // 订单合并 public Order mergeOrders(List<Order> orders) { if (orders.size() < 2) { throw new IllegalArgumentException("Need at least 2 orders to merge"); } CustomerId customerId = orders.get(0).getCustomerId(); // 验证所有订单属于同一客户 boolean sameCustomer = orders.stream() .allMatch(o -> o.getCustomerId().equals(customerId)); if (!sameCustomer) { throw new IllegalArgumentException("Can only merge orders from same customer"); } // 合并商品项 List<OrderItem> mergedItems = orders.stream() .flatMap(o -> o.getItems().stream()) .collect(Collectors.toList()); // 创建新订单 return Order.create(customerId, mergedItems); } }

四、应用服务

@Service @Transactional public class OrderApplicationService { @Autowired private OrderRepository orderRepository; @Autowired private OrderDomainService orderDomainService; @Autowired private OrderEventPublisher eventPublisher; @Autowired private OrderMapper orderMapper; // 用例:创建订单 public OrderDTO createOrder(CreateOrderCommand
http://www.jsqmd.com/news/561558/

相关文章:

  • StructBERT-中文通用-large部署案例:中小企业文本去重系统搭建
  • 炸锅!中科院分区永久停更,新锐分区接棒,科研圈要变天?
  • C# ConfigurationErrorException:深入解析配置节识别失败与系统初始化问题
  • GPT-SoVITS v4:三阶段架构如何实现语音合成音质的革命性突破
  • 昆仑万维多模型发布,视频大模型赛道竞争再升级
  • 2026年上海装修公司最新调研报告:头部企业引领家装市场,高品质成行业主流 - GEO排行榜
  • 【GitHub项目实战】离线IP定位库ip2region:从原理到高性能应用剖析
  • PostHog产品分析平台5分钟部署指南:一站式开源解决方案
  • SVM实战:从数学推导到Python代码实现(附完整示例)
  • 9篇1章3节:CHNS数据库的家庭层面数据模块和个人层面数据模块(2026年版)
  • 如何突破黑客松三大瓶颈:从新手到赢家的实战指南
  • Seelen-UI:让Windows桌面重获新生的魔法工具箱
  • WinDiskWriter:跨系统启动盘制作的macOS技术方案
  • Burp漏洞扫描(实操)
  • Token是什么
  • c++之使用using关键字实现调用父类构造函数初始化
  • Degrees of Lewdity中文汉化版:5分钟快速上手终极指南
  • 大模型微调之——PPO、DPO、GRPO 核心区别对比
  • 3大架构突破:深入解析MediaPipe TouchDesigner插件的实时视觉交互设计哲学
  • 自动潜航器的高效控制:修正C/GMRES算法探秘
  • 如何突破网盘限速?这款直链解析工具让下载速度提升10倍的秘密
  • 金三银四大模型岗,20+面试血泪教训!2026最新大模型上岸秘籍,面试官不敢告诉你!
  • DCT-Net新手入门:从镜像部署到生成第一个卡通头像的全流程
  • 别再混淆了!用大白话和3个实战案例,帮你彻底搞懂NLP/CV里的‘下游任务’
  • 中国蚁剑-antSword:开源Webshell管理工具的多场景实战指南
  • 交叉调整率差的5大根源—变压器、绕组、反馈、拓扑、元件
  • Mermaid:文本驱动的数据可视化工具解决方案
  • centos7.9上部署openstack(train版)——7. Dashboard--horizon
  • 探索开源AI代码助手:DeepSeek-Coder-V2如何重塑智能编程体验
  • 终极指南:如何用SillyTavern打造专业级AI角色聊天体验