Skip to content

Day 06 - 实体(Entity)与标识

📖 今日目标

  1. 理解实体(Entity)的概念和特征
  2. 掌握实体的标识设计
  3. 学习实体的代码实现模式

核心概念

什么是实体?

实体(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实体

  • 实体封装业务逻辑和状态
  • 实体有行为
  • 实体维护自身完整性

贫血模型的问题

  1. 业务逻辑散落在各处,难以维护
  2. 违反封装原则
  3. 难以测试业务规则
  4. 模型不反映业务语义

正确做法

java
// 贫血模型 ❌
order.setStatus(OrderStatus.PAID); // 业务逻辑在哪里?

// DDD实体 ✅
order.pay(); // 行为封装在实体中

问题2:如何设计实体的标识?

答案:

标识设计考虑因素:

  1. 业务标识 vs 技术标识

    • 业务标识:订单号、身份证号(业务含义)
    • 技术标识:UUID、自增ID(无业务含义)
  2. 分布式环境下的标识

    • 使用UUID/Snowflake避免单点
    • 考虑标识的存储和索引
  3. 标识的格式

    • 人类可读:订单号包含日期+序号
    • 便于调试和追溯
  4. 标识的不可变性

    • 标识一旦生成不应改变
    • 创建后不可修改

推荐实践

java
public class OrderId {
    private final String value;
    
    public static OrderId of(String value) {
        return new OrderId(value);
    }
    
    public String getValue() { return value; }
}

实践思考

🎯 思考题

在电商系统中,识别以下对象是实体还是值对象:

  1. 订单(Order)
  2. 地址(Address)
  3. 购物车(ShoppingCart)
  4. 商品(Product)
  5. 金额(Money)

说明你的判断理由。


📚 扩展阅读


📅 学习进度

Day主题状态
01-05战略设计基础
06实体(Entity)与标识
07值对象(Value Object)
.........
30复习与总结

本文为DDD 30天学习计划第6天内容

📚 导航

| ← Day 05 - 上一章 | Day 07 - 下一章 →

最后更新: