Appearance
Day 06 - 实体(Entity)与标识
📖 今日目标
- 理解实体(Entity)的概念和特征
- 掌握实体的标识设计
- 学习实体的代码实现模式
核心概念
什么是实体?
实体(Entity) 是DDD战术设计中最核心的概念之一。它表示具有唯一标识且在整个生命周期中保持连续性的对象。
实体的核心特征:
- 唯一标识:每个实体实例都有唯一标识
- 连续性:标识不变,属性可以变化
- 生命周期:从创建到销毁的完整过程
关键要点
1. 实体 vs 值对象
| 对比项 | 实体 | 值对象 |
|---|---|---|
| 标识 | 有唯一标识 | 无标识,仅由属性定义 |
| 相等性 | 标识相同则相等 | 属性相同则相等 |
| 可变性 | 属性可变 | 不可变,创建后不变化 |
| 生命周期 | 独立生命周期 | 附属生命周期 |
2. 标识的设计
标识类型选择:
- 自然标识:业务中的自然编号(订单号、身份证号)
- 代理标识:系统生成的ID(UUID、数据库自增ID)
标识设计原则:
- 标识应该是不可变的
- 标识在实体创建时就确定
- 考虑标识的格式和长度
3. 实体的职责
实体应该:
- 封装业务规则和业务状态
- 维护自身的完整性
- 暴露有意义的业务行为
4. 实体设计示例
java
public class Order {
private OrderId id;
private CustomerId customerId;
private List<OrderItem> items;
private OrderStatus status;
public void addItem(Product product, int quantity) {
// 业务规则校验
if (status != OrderStatus.DRAFT) {
throw new BusinessException("只有草稿状态可以添加商品");
}
// ...
}
}面试考点
问题1:DDD中实体和贫血模型有什么区别?
答案:
贫血模型(Anemic Domain Model):
- 实体只有getter/setter
- 所有业务逻辑放在Service层
- 实体只是数据容器
DDD实体:
- 实体封装业务逻辑和状态
- 实体有行为
- 实体维护自身完整性
贫血模型的问题:
- 业务逻辑散落在各处,难以维护
- 违反封装原则
- 难以测试业务规则
- 模型不反映业务语义
正确做法:
java
// 贫血模型 ❌
order.setStatus(OrderStatus.PAID); // 业务逻辑在哪里?
// DDD实体 ✅
order.pay(); // 行为封装在实体中问题2:如何设计实体的标识?
答案:
标识设计考虑因素:
业务标识 vs 技术标识:
- 业务标识:订单号、身份证号(业务含义)
- 技术标识:UUID、自增ID(无业务含义)
分布式环境下的标识:
- 使用UUID/Snowflake避免单点
- 考虑标识的存储和索引
标识的格式:
- 人类可读:订单号包含日期+序号
- 便于调试和追溯
标识的不可变性:
- 标识一旦生成不应改变
- 创建后不可修改
推荐实践:
java
public class OrderId {
private final String value;
public static OrderId of(String value) {
return new OrderId(value);
}
public String getValue() { return value; }
}实践思考
🎯 思考题:
在电商系统中,识别以下对象是实体还是值对象:
- 订单(Order)
- 地址(Address)
- 购物车(ShoppingCart)
- 商品(Product)
- 金额(Money)
说明你的判断理由。
📚 扩展阅读
- 《领域驱动设计》第5章:软件中表示的模型
- 《实现领域驱动设计》第6章:实体
- DDD Entities - Martin Fowler
📅 学习进度
| Day | 主题 | 状态 |
|---|---|---|
| 01-05 | 战略设计基础 | ✅ |
| 06 | 实体(Entity)与标识 | ✅ |
| 07 | 值对象(Value Object) | ⬜ |
| ... | ... | ... |
| 30 | 复习与总结 | ⬜ |
