DDD领域驱动设计

概念

DDD(domain-driven design)(领域驱动设计)。

特点:
以业务为中心,强调开发和业务必须使用同一套术语,即通用语言。

模型分层和核心构件
核心构件作用与特点
实体具有唯一标识的对象(如订单ID),它的状态会随时间变化(如订单状态从"待支付"变为"已发货")。
值对象没有唯一标识,仅描述事物的属性(如"收货地址"、“金额”)。它是不可变的,用完即弃,更轻量、更安全。
聚合根(充血实体)一组相关对象的"门面"或"入口"。外部访问聚合内的任何对象,都必须通过聚合根,这确保了数据的一致性。
领域服务处理一些不适合放在实体里的业务逻辑,比如"转账"这种涉及多个账户的操作。

订单业务示例

1. 值对象 (Value Object)

DDD原则:把经常绑在一起的数据打包,用对象代替散落的字段。

// 金额是一个值对象,它有自己的业务规则(比如不能为负数)
@Getter
@AllArgsConstructor
public class Money {
    private BigDecimal amount;

    public Money(BigDecimal amount) {
        if (amount.compareTo(BigDecimal.ZERO) < 0) {
            throw new IllegalArgumentException("金额不能为负数");
        }
        this.amount = amount;
    }
}

2. 领域事件 (Domain Event)

DDD 原则:实体只负责自己的状态变更,不关心后续要做什么(解耦)。

// 记录“订单已支付”这个事实
@Getter
@AllArgsConstructor
public class OrderPaidEvent {
    private Long orderId;
    private Money paidAmount;
}

3. 充血实体 / 聚合根 (Aggregate Root)

DDD原则:没有 Setter!只有业务方法。把校验逻辑锁死在内部。

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED) // 私有构造,防止外部随意 new
public class Order {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Enumerated(EnumType.STRING)
    private OrderStatus status; // 使用枚举,告别魔法数字

    private BigDecimal totalAmount;

    // 忽略 JPA 持久化,仅用于内存中暂存事件
    @Transient
    private List<Object> domainEvents = new ArrayList<>();

    // 工厂方法:创建订单
    public static Order create(BigDecimal totalAmount) {
        Order order = new Order();
        order.totalAmount = totalAmount;
        order.status = OrderStatus.PENDING_PAYMENT;
        return order;
    }

    // 【核心业务行为】:支付订单
    public void pay(Money payAmount) {
        // 1. 状态校验(强一致性)
        if (this.status != OrderStatus.PENDING_PAYMENT) {
            throw new BusinessException("订单当前状态不允许支付");
        }
        // 2. 金额校验
        if (this.totalAmount.compareTo(payAmount.getAmount()) != 0) {
            throw new BusinessException("支付金额与订单金额不符");
        }

        // 3. 状态流转
        this.status = OrderStatus.PAID;

        // 4. 注册领域事件(告诉外界:我支付完了,你们看着办)
        this.domainEvents.add(new OrderPaidEvent(this.id, payAmount));
    }
}

4. 仓储接口 (Repository)

DDD原则:领域层定义接口,基础设施层去实现(隔离数据库)。

// 只需要暴露聚合根的操作
public interface OrderRepository {
    Order findById(Long id);
    void save(Order order);
}

5. 应用服务 (Application Service)

DDD原则:不含任何 if-else!只做“查出实体 -> 调用方法 -> 保存 -> 发事件”的流水线工作。

@Service
public class OrderAppService {

    @Autowired
    private OrderRepository orderRepository;
    
    @Autowired
    private ApplicationEventPublisher eventPublisher; // Spring 的事件发布器

    public void payOrder(Long orderId, BigDecimal amount) {
        // 1. 获取聚合根
        Order order = orderRepository.findById(orderId);

        // 2. 调用充血实体的业务方法(Service 完全不知道支付规则是什么)
        order.pay(new Money(amount));

        // 3. 持久化
        orderRepository.save(order);

        // 4. 发布领域事件(自动触发下游业务)
        // 把实体内存中积攒的事件拿出来发布
        // 这里简化处理,直接发布
        eventPublisher.publishEvent(new OrderPaidEvent(orderId, new Money(amount)));
    }
}

6. 事件监听器 (Event Listener)

DDD原则:后续动作(发短信、扣积分)完全独立,随时可以增删。

@Component
public class OrderEventHandler {

    // 监听订单支付成功事件
    @EventListener
    public void onOrderPaid(OrderPaidEvent event) {
        System.out.println("【积分服务】:用户支付成功,订单号:" + event.getOrderId() + ",增加 100 积分!");
        System.out.println("【短信服务】:用户支付成功,发送短信通知...");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值