You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

DDD中Explicit State Modeling落地实践相关疑问求解

显式状态建模落地疑问咨询

背景

我最近在研究聚合根的状态与生命周期管理,发现和状态模式相比,采用显式状态建模(Explicit State Modeling/Explicit Modeling)的方案更简洁,能够让领域内的显式概念自行处理对应行为,完全符合我的预期。我参考了Scott Millett与Nick Tune所著《领域驱动设计的模式、原则与实践》第16章衍生的相关内容,以及对应的代码样例。

不过现有资料对这套方案的介绍普遍比较简略,我作为DDD入门者在落地过程中出现了概念混淆,整理了当前的几个核心困惑:

  • 按照参考示例,若要查询同时包含开门、关门状态的全部门列表,返回的结果集应映射到哪个领域实体?
  • 如果所有显式状态模型都是实体/聚合,对应的聚合根是什么?
  • 聚合根和这些显式建模的实体之间不存在引用关系是否符合规范?
  • 若聚合根(比如通用的Door实体)返回显式状态实体,仓储如何在不暴露实体/聚合状态的前提下完成持久化?
  • 这些显式状态实体是否本身就是各自聚合的根?

我暂未梳理清楚完整逻辑,无法提供自有代码,希望下文的参考片段可以作为判断依据。如果有结合显式建模实现其他DDD组件的示例项目推荐也非常有帮助,我已经查阅了大量相关仓库,但大多不符合需求。
注:我当前未使用CQRS


参考代码示例

Medium文章参考示例

interface ClosableDoor 
{ 
    public function close(); 
}
// 显式状态实现(第三次优化版本)
class CloseDoorService() 
{ 
    // 注入依赖
   
    public function execute($doorId) 
    {
        $door = $this->doorRepository->findClosableOfId($doorId);
        if (!$door) { 
            throw new ClosableDoorNotFound(); 
        }
        $door = $door->close(); 
        $this->doorRepository->persist($door); 
    }
}

书籍参考示例

// 以下实体集合替代了原本使用状态模式的OnlineTakeawayOrder实体
public class InKitchenOnlineTakeawayOrder
{
 public InKitchenOnlineTakeawayOrder(Guid id, Address address)
 {
 // 初始化逻辑省略
 this.Id = id;
 this.Address = address;
}
 public Guid Id { get; private set; }
 public Address Address { get; private set; }
 // 仅包含自身实际支持的方法
 // 返回新的状态实例,强制调用方感知状态变化
 public InOvenOnlineTakeawayOrder Cook()
 {
 // 业务逻辑省略
 return new InOvenOnlineTakeawayOrder(this.Id, this.Address);
 }
}
public class InOvenOnlineTakeawayOrder
{
 public InOvenOnlineTakeawayOrder(Guid id, Address address)
 {
 // 初始化逻辑省略
 this.Id = id;
 this.Address = address;
 }
 public Guid Id { get; private set; }
 public Address Address { get; private set; }
 public CookedOnlineTakeawayOrder TakeOutOfOven()
 {
 // 业务逻辑省略
 return new CookedOnlineTakeawayOrder(this.Id, this.Address);
 }
}

疑问解答

  1. 多状态列表查询的实体映射问题
    不需要把查询结果强制映射到写路径的领域实体,显式状态建模的核心是约束写操作的领域规则,读场景下如果需要聚合多状态的统一视图,可以直接定义专门的读模型DTO承载返回结果,和是否使用CQRS没有冲突。如果一定要在领域层复用统一抽象,可以定义包含所有状态共有属性的Door接口/抽象类,开放、关闭状态的实体都实现该接口,查询时返回接口类型的集合即可。

  2. 显式状态模型的聚合根归属
    所有同生命周期、同业务ID的显式状态实体,本质是同一个聚合根在不同阶段的形态,聚合根的ID始终唯一(比如订单ID、门ID),不需要为每个状态单独定义聚合根,可以把这些状态类理解为聚合根的「类型化状态实例」,核心身份标识始终统一。

  3. 聚合根与显式状态实体无引用的合规性
    完全符合规范。显式状态建模的核心就是用类型系统替代隐式的状态字段,原本聚合根里的state枚举被替换成了不同的类型实现,二者不需要额外的引用关系,每个状态实体本身就是特定阶段的聚合根实例。

  4. 仓储持久化的实现方案
    可以让仓储依赖统一的抽象接口,不需要感知具体的状态类型,持久化时只需要提取所有状态实例共有的身份标识、属性、状态枚举值存入数据库即可,状态到枚举值的映射可以放在状态类内部实现(比如每个状态类暴露一个只读的StateCode属性),不会泄露实体的内部业务逻辑。查询时,仓储可以根据数据库存储的状态字段,实例化对应类型的状态实体返回即可,这部分逻辑完全封装在仓储内部,领域层不需要感知。

  5. 显式状态实体是否是独立聚合根
    不是。所有共享同一个业务ID的状态实例,都是同一个业务聚合的不同表现形态,聚合根的业务边界始终是同一个业务对象(比如同一个门、同一个订单),不会因为状态变化改变聚合边界。只有当某个状态下的实体产生了完全独立的生命周期、业务规则、身份标识时,才需要拆分为独立的聚合根。


内容的提问来源于stack exchange,提问作者Mazen Elkashef

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 08:54:05