到底为什么PHP要有领域实体 (Entity)?
2026/6/23 2:22:22 网站建设 项目流程

它的本质是:**领域实体 (Entity) 是为了在代码中锚定“我是谁” (Identity),而不仅仅是“我有什么” (Attributes)

  • 核心定义:Entity 是一个通过唯一标识符 (ID)来区分的对象。即使它的所有属性都变了,只要 ID 不变,它依然是同一个实体。
  • 对比值对象 (Value Object)
    • 值对象:由属性值定义。两个地址如果街道、城市一样,它们就是相等的 ($addr1 == $addr2)。没有独立身份。
    • 实体:由ID定义。两个用户即使名字、年龄完全一样,只要 ID 不同,他们就是不同的人 ($user1 !== $user2)。
  • 存在理由
    1. 追踪变化 (Change Tracking):因为实体有唯一身份,系统可以记录“张三”从“单身”变成了“已婚”。如果没有实体概念,这只是两条不同的记录,无法体现演变过程。
    2. 承载行为 (Behavior Carrier):实体不仅是数据容器,更是业务规则的守护者。它知道如何修改自己的状态才合法(如User::changeEmail()内部校验格式)。
    3. 长期持久化 (Long-term Persistence):实体通常对应数据库中的一行,需要在多次请求、甚至数年间保持身份一致。
  • 核心逻辑别把 Entity 当成数据库表的简单映射。它是业务世界中有生命、有历史、有身份的核心角色。数组和 DTO 是死的快照,Entity 是活的参与者。

如果把业务系统比作户籍管理系统

  • 值对象 (VO):是家庭住址
    • “北京市朝阳区xx路1号”。
    • 如果两个人住这里,这个地址是一样的。
    • 地址本身没有“身份证”,它依附于人。
  • 领域实体 (Entity):是公民本人
    • 每个人有唯一的身份证号 (ID)
    • 即使他改了名字、换了住址、长了皱纹(属性变化),他的身份证号不变,他依然是同一个人。
    • 警察(系统)通过身份证号追踪他的犯罪记录、婚姻状况(状态历史)。
    • 核心逻辑Entity 提供了时间的连续性。它让系统记得“过去的那个他”和“现在的他是同一个”。

一、身份与连续性:为什么 ID 如此重要?

1. 唯一性约束 (Uniqueness Constraint)
  • 机制:Entity 必须有一个全局或聚合内唯一的 ID。
  • 价值
    • 在分布式系统中,ID 是跨服务引用同一事物的唯一依据。
    • 在数据库中,ID 是主键,确保数据不重复。
    • 代码体现if ($user1->getId() === $user2->getId()) { ... }
2. 状态演变 (State Evolution)
  • 场景:订单状态流转Created -> Paid -> Shipped -> Completed
  • Entity 作用
    • 订单实体持有当前状态。
    • 它提供方法pay(),内部检查是否处于Created状态,然后转为Paid
    • 价值:保证了状态变更的合法性可追溯性。如果是纯数据数组,任何人都可以把状态改成Completed而跳过Paid,导致业务漏洞。
3. 相等性判断 (Equality Check)
  • 规则:实体的相等性只比较 ID,不比较其他属性。
    publicfunctionequals(self$other):bool{return$this->id===$other->id;}
  • 价值:即使两个用户对象是从不同查询加载的,属性略有差异(如一个加载了详情,一个没加载),只要 ID 相同,业务上就视为同一人。

💡 核心洞察Entity 是时间轴上的一个点。它连接了过去、现在和未来。ID 是那条贯穿时间的线。


二、行为封装:Entity 不是数据结构

1. 保护不变量 (Protecting Invariants)
  • 原则:Entity 的任何公共方法执行后,对象必须处于合法状态。
  • 示例
    classOrder{publicfunctionaddItem(Product$product):void{if($this->status!==OrderStatus::CREATED){thrownewLogicException("Cannot add items to a completed order.");}$this->items[]=$product;$this->recalculateTotal();// 自动维护一致性}}
  • 价值:将业务规则内聚在实体内部,防止外部代码破坏业务逻辑。
2. 领域事件发布 (Domain Event Publishing)
  • 场景:用户注册成功后,需要发送欢迎邮件、增加积分。
  • Entity 作用
    publicfunctionregister():void{// ... 逻辑$this->recordEvent(newUserRegistered($this->id));}
  • 价值:实体不仅改变自身状态,还通知外界“我发生了变化”,实现解耦。
3. 延迟加载与关联管理
  • 场景:获取用户时,不立即加载其所有订单。
  • Entity 作用:内部持有代理对象,只有在访问$user->getOrders()时才触发查询。
  • 价值:优化性能,同时保持对象模型的完整性。

三、与 ORM 模型的区别:为什么不能直接用 Eloquent/Doctrine?

很多开发者混淆了ORM 模型领域实体

维度ORM 模型 (Active Record/Data Mapper)领域实体 (Domain Entity)
主要职责数据持久化 (CRUD)业务逻辑与规则
依赖依赖数据库连接/框架无外部依赖 (Pure PHP)
关注点怎么存 (How to Save)是什么 (What it Is)
方法save(),delete(),find()changePassword(),activate()
测试需集成测试 (连库)单元测试 (内存中)
最佳实践作为基础设施层作为领域层核心

现代架构建议

  • 分离:领域实体应该是纯粹的 PHP 对象,不包含 SQL 逻辑。
  • 映射:使用 Repository 模式,将 ORM 模型的数据转换为领域实体,或在实体上使用 Trait 辅助持久化,但尽量保持实体纯净。
  • 价值:这样即使更换数据库或 ORM 框架,核心业务逻辑(实体)无需修改。

四、认知牢笼:常见误区

1. 误区:“Entity 就是数据库表的一行。”
  • 真相
    • 表是存储结构,Entity 是业务概念。
    • 一个 Entity 可能映射到多张表(如继承策略),或多张表组成一个 Entity。
    • 对策:从业务语言命名 Entity,而非数据库表名。
2. 误区:“所有对象都是 Entity。”
  • 真相
    • 如果对象没有唯一身份,且由其属性定义相等性,它是值对象 (Value Object)(如金钱、颜色、坐标)。
    • 对策:区分 Entity 和 VO。VO 更轻量,不可变,适合计算。
3. 误区:“Entity 应该包含所有业务逻辑。”
  • 真相
    • 涉及多个实体交互的逻辑(如转账:从 A 扣钱,给 B 加钱)应放在领域服务 (Domain Service)中。
    • 对策:Entity 处理单个个体的规则,Service 处理群体协作。
4. 误区:“Entity 必须有 Getter/Setter。”
  • 真相
    • 过度暴露 Getter/Setter 会导致贫血模型
    • 对策:提供意图明确的方法(如promoteToAdmin()而非setRole('admin'))。
5. 误区:“PHP 性能差,不适合搞复杂的 Entity。”
  • 真相
    • 现代 PHP + OPcache 性能足够支撑大多数业务。
    • 清晰的 Entity 模型能减少 Bug,降低维护成本,这比微小的 CPU 节省更有价值。
    • 对策:合理设计聚合根,避免加载过大的对象图。

🚀 总结:原子化“领域实体”全景图

维度关键点
本质通过唯一 ID 标识的业务对象,具有连续性和状态
核心价值身份追踪、业务规则封装、状态合法性保障
关键特征唯一 ID、基于 ID 的相等性、可变状态、行为方法
与 VO 区别Entity 看 ID,VO 看值
与 ORM 区别Entity 关注业务,ORM 关注持久化
PHP 隐喻Citizen with ID Card (Entity) vs. Home Address (VO)
公式Identity = (Unique_ID × State_History) ^ Business_Rules

终极心法

领域实体的本质,是“业务世界的锚点”。
它在流动的数据中,确立了不变的自我。
它让系统拥有了记忆,让逻辑拥有了归属。
于身份中见连续,于行为中见规则;以实体为尺,解离散之牛,于领域建模中,求真实之真。

行动指令

  1. 识别实体:在你的项目中,找出哪些对象有唯一 ID 且状态会变(如 User, Order, Product)。
  2. 提取行为:检查这些对象的 Setter,看能否将其重构为更具语义的方法(如cancel()替代setStatus('cancelled'))。
  3. 区分 VO:找出哪些对象只是值的组合(如 Money, DateRange),将其改为不可变的值对象。
  4. 思维升级:记住,Entity 是你业务核心的代言人。保护好它的不变量,就是保护好业务的底线。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询