#DDD核心概念精讲
#一、实体(Entity)
#1.1 什么是实体
实体:具有唯一标识的对象,其生命周期中标识保持不变,属性可能变化。
class Order {
private OrderId id; // 唯一标识,整个生命周期不变
private OrderStatus status; // 属性可变
private Money amount; // 属性可变
// 通过 ID 判断相等,不是通过属性
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Order order = (Order) o;
return id.equals(order.id);
}
@Override
public int hashCode() {
return id.hashCode(); // 基于 ID 计算哈希
}
}#1.2 实体的特征
| 特征 | 说明 |
|---|---|
| 唯一标识 | 通过 ID 识别,不是属性 |
| 生命周期 | 从创建到销毁,ID 不变 |
| 可变性 | 属性可以变化 |
| 连续性 | 可以追踪变化历史 |
#二、值对象(Value Object)
#2.1 什么是值对象
值对象:没有唯一标识,通过属性值确定其身份,不可变。
// Money 是值对象
class Money {
private final BigDecimal amount;
private final Currency currency;
private Money(BigDecimal amount, Currency currency) {
this.amount = amount;
this.currency = currency;
}
public Money add(Money other) {
if (!this.currency.equals(other.currency)) {
throw new IllegalArgumentException("Currency mismatch");
}
return new Money(this.amount.add(other.amount), this.currency);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Money money = (Money) o;
return amount.equals(money.amount) && currency.equals(money.currency);
}
@Override
public int hashCode() {
return Objects.hash(amount, currency);
}
}#2.2 实体 vs 值对象
| 维度 | 实体 | 值对象 |
|---|---|---|
| 标识 | 有唯一标识 | 无标识 |
| 相等性 | 基于标识 | 基于属性 |
| 可变性 | 可变 | 不可变 |
| 生命周期 | 有 | 无 |
| 设计原则 | 实体可以引用值对象 | 值对象通常作为实体的属性 |
#三、聚合(Aggregate)
#3.1 什么是聚合
聚合:一组相关对象的集合,作为数据修改的单元。聚合有一个聚合根,外部只能通过聚合根访问内部对象。
// Order 是聚合根
class Order extends BaseAggregateRoot {
private OrderId id;
private List<OrderItem> items; // OrderItem 不能单独存在
// 外部只能通过 Order 访问 OrderItem
public void addItem(Product product, int quantity) {
if (this.status != OrderStatus.DRAFT) {
throw new OrderCannotModifyException();
}
// 业务规则内聚
OrderItem existing = findItem(product.getId());
if (existing != null) {
existing.increaseQuantity(quantity);
} else {
items.add(new OrderItem(product.getId(), product.getPrice(), quantity));
}
recalculateTotal();
}
// 内部方法
private OrderItem findItem(ProductId productId) {
return items.stream()
.filter(i -> i.getProductId().equals(productId))
.findFirst()
.orElse(null);
}
}#3.2 聚合的规则
聚合设计原则:
1. 聚合边界内保持强一致性
2. 跨聚合只能通过 ID 引用
3. 聚合是事务边界
4. 聚合应该小而内聚#四、领域服务(Domain Service)
#4.1 什么时候用领域服务
领域服务:当某个业务逻辑不属于任何实体或值对象时,使用领域服务。
// 跨聚合的业务逻辑:转账
class TransferService {
public void transfer(Account from, Account to, Money amount) {
// 涉及两个 Account 聚合,无法放在任何一个聚合中
from.withdraw(amount);
to.deposit(amount);
}
}#4.2 领域服务 vs 应用服务
| 维度 | 领域服务 | 应用服务 |
|---|---|---|
| 位置 | 领域层 | 应用层 |
| 职责 | 业务逻辑 | 编排协调 |
| 依赖 | 领域对象 | 领域服务、仓储 |
#五、领域事件(Domain Event)
#5.1 什么是领域事件
领域事件:领域中发生的、业务有意义的事件。
// 领域事件
record OrderCreatedEvent(
OrderId orderId,
UserId userId,
Money amount,
Instant occurredAt
) implements DomainEvent {}
// 聚合根中发布事件
class Order extends BaseAggregateRoot {
public void create(CreateOrderCommand cmd) {
Order order = new Order(cmd.getUserId());
order.items.addAll(cmd.getItems());
order.calculateTotal();
// 发布领域事件
registerEvent(new OrderCreatedEvent(
order.getId(),
order.getUserId(),
order.getTotalAmount(),
Instant.now()
));
return order;
}
}#5.2 事件订阅
// 订阅领域事件
@EventListener
class InventoryService {
@Async
public void handleOrderCreated(OrderCreatedEvent event) {
// 扣减库存
for (OrderItem item : event.getItems()) {
inventoryService.reserve(item.getProductId(), item.getQuantity());
}
}
}#六、仓储(Repository)
#6.1 仓储的职责
仓储:聚合的持久化抽象,只操作聚合根。
// 仓储接口(在领域层定义)
interface OrderRepository {
void save(Order order);
Optional<Order> findById(OrderId id);
List<Order> findByUserId(UserId userId);
}
// 仓储实现(在基础设施层)
class JpaOrderRepository implements OrderRepository {
@Override
public void save(Order order) {
jpaTemplate.save(order);
}
}#6.2 仓储 vs DAO
| 维度 | 仓储 | DAO |
|---|---|---|
| 范围 | 聚合根 | 任意表 |
| 抽象程度 | 高(领域概念) | 低(技术概念) |
| 接口位置 | 领域层 | 基础设施层 |
| 实现位置 | 基础设施层 | 数据层 |
#七、限界上下文(Bounded Context)
#7.1 什么是限界上下文
限界上下文:领域模型有明确边界的部分,每个上下文有自己的领域模型和 Ubiquitous Language。
┌──────────────────┐ ┌──────────────────┐
│ 电商上下文 │ │ 仓库上下文 │
│ │ │ │
│ Order │ │ Warehouse │
│ Customer │ │ Inventory │
│ Product │ │ Shelf │
│ │ │ │
│ 同一个 Product │ │ 同一个 Product │
│ 在不同上下文中 │ │ 含义不同 │
│ 含义不同 │ │ │
└────────┬─────────┘ └────────┬─────────┘
│ │
│ 上下文映射 │
└───────────────────────┘#7.2 上下文映射类型
| 类型 | 说明 |
|---|---|
| 共享内核 | 两个上下文共享部分模型 |
| 客户-供应商 | 上游发布,下游消费 |
| 防腐层 | 转换器隔离不同上下文 |
| 独立模式 | 各自独立,通过事件异步交互 |