首页 > 解决方案 > DDD 中的显式状态建模

问题描述

我一直在寻找管理 Root Aggregate 状态/生命周期,并发现了一些关于 usingExplicit State ModelingExplicit Modelingover的好处的内容State Pattern,它更简洁,我喜欢如何让我的域的明确概念处理它们自己的行为。

我读到的其中一篇文章是受“领域驱动设计的模式、原则和实践 - Scott Millett 和 Nick Tune”一书第 16 章的影响(这里是完整示例的代码示例)。

问题是这个想法描述得非常简单,没有太多内容,当我开始实施它时出现了,考虑到我是 DDD 的新手,概念开始重叠,这里有一些我想问的问题我希望 DDD 中更有经验的工程师会有所帮助,或者至少有人比我更好地解释了文本。

  1. 按照文章的示例,我将如何检索所有门(打开和关闭)的列表,这个结果集将映射到什么域实体?
  2. 如果所有显式状态模型都是实体/聚合,那么根聚合是什么?
  3. Root Aggregate 和那些显式建模的实体之间没有引用是否正常?
  4. 如果聚合根(比方说通用门实体)返回显式状态实体,存储库如何在不暴露实体或聚合状态的情况下保存它?
  5. 还是所有这些显式实体都是它们自己聚合的根?

我不希望得到以上所有答案,我只是分享被困在的想法,所以你可以看到我的立场,因为我分享代码有太多歧义,但我希望片段来自文章和书可以提供帮助。

一个 git 存储库或一个示例项目解决了其他具有显式建模的 DDD 组件将如何真正有用(我检查了一百万个存储库,但 90% 没有运气)。

注意:我没有使用 CQRS

来自媒体文章的示例:

interface ClosableDoor 
{ 
    public function close(); 
}
// Explicit State. (third attempt) 
class CloseDoorService() 
{ 
    // inject dependencies
   
    public function execute($doorId) 
    {
        $door = $this->doorRepository->findClosableOfId($doorId);
        if (!$door) { 
            throw new ClosableDoorNotFound(); 
        }
        $door = $door->close(); 
        $this->doorRepository->persist($door); 
    }
}

书中的例子:

// these entities collectively replace the OnlineTakeawayOrder entity (that used the state pattern)
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; }
 // only contains methods it actually implements
 // returns new state so that clients have to be aware of it
 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);
 }
}

标签: domain-driven-designaggregateroot

解决方案


注意:我没有使用 CQRS

我认为这是你面临的最大挑战。

如果您没有尝试将它们用于可能不受为特定用例设计的显式模型的查询,那么为正在实现的用例检索显式建模的实体不会引起如此头痛。

我使用支持“表拆分”的实体框架,可以在这种情况下提供帮助。使用它,许多实体可以映射到同一个表,但每个实体都可以处理表中字段的子集并具有专用的行为。

// 用于一般查询

class Door
{
    public Guid Id { get; private set; }
    public State State { get; private set; }

    // other props that user may want included in query but are not
    // relevant to opening or closing a door
    public Color Color { get; private set; }
    public Dimensions Dimensions { get; private set; }
    public List<Fixing> Fixings { get; private set; }
}

class DoorRepository
{
    List<Door> GetDoors()
    {
        return _context.Doors;
    }
}

// 用于开门用例

class ClosedDoor
{
    public Guid Id { get; private set; }
    public State State { get; private set; }

    public void Open()
    {
        State = State.Open;
    }
}

class ClosedDoorRepository
{
    List<ClosedDoor> GetClosedDoors()
    {
        return _context.ClosedDoors.Where(d => d.State == State.Closed);
    }
}

// 用于关门用例

class OpenDoor
{
    public Guid Id { get; private set; }
    public State State { get; private set; }

    public void Close()
    {
        State = State.Closed;
    }
}

class OpenDoorRepository
{
    List<OpenDoor> GetOpenDoors()
    {
        return _context.OpenDoors.Where(d => d.State == State.Open);
    }
}

推荐阅读