AggregateRoot A 验证需要来自 AggregateRoot B 的信息

问题描述

我是 DDD 世界的新手,我想知道如何处理我有一个聚合根 (AggregateRootA) 的情况,它需要来自其他聚合根 (AggregareRootB) 的一些信息才能执行某些操作(例如更改 AggregateRootA 的状态)。

如果我尝试使用更真实的场景,也许这会更清楚......让我们考虑一个预订系统。在系统中,我们有 Reservation 聚合根和 Resource 聚合根。资源具有可用/不可用状态。系统允许创建预留并向其添加资源(在代码中当然是通过 Id 引用来完成的)。下一步是接受保留,在这一步中,保留必须检查其所有资源是否都处于可用状态。让我们说它不能在添加步骤中完成,或者只是该资源在它已经处于保留状态时改变了它的状态。如何执行此类验证?

我想出了一个解决方案,其中 Reservation 可以访问 ResourceRepository,但我发现不应允许 AggregateRoot 在 DDD 中具有此类访问权限。

我的第二个想法是将验证移至 applicationservice,但对我来说这种验证看起来像域逻辑,因此应将其放置在域模型中。

我最后的想法是,也许我应该编写专用的 DomainService 来处理这种情况,但再次……DomainService 是否应该可以访问存储库?

解决方法

我认为您正在接近最后一段中的要点,但问题不是是否应该域服务可以访问存储库,而更多的是关于可以的问题> 它有(在你的技术实现中)?在技​​术基础上评估该方法的利弊,如果没有真正糟糕的事情出现,那就去做吧。对我来说,这似乎就是你的大脑一直告诉你无论如何都要去做的事情。

然后随着时间的推移,你对周围领域、有界上下文、语言等的理解变得更好(而且会),你总是可以重构和重构。没有什么是一成不变的。

,

这是一个非常常见且易于解决的问题,但您需要退后一步。

您基本上应该有两个有界上下文 - 保留和资源。 保留不能访问任何其他有界上下文的任何类/服务/对象,因为它违反了封装规则。

您应该改为在 resources 有界上下文中创建查询处理程序。 资源 应该共享查询和响应类,这些类将提供您需要的信息——公共 API,它是您的域之间的契约。最重要的是,reservations bounded context 不应该知道 resources 的任何实现细节。

PHP 示例:

资源域:

请求信息:

class IsResourceAvaiableQuery
{
    private int $resourceId;

    __construct(int $resourceId)
    {
        $this->resourceId = $resourceId;
    }

    public function getResourceId(): int 
    {
        return $this->resourceId;
    }
}

要获得一个不应该是简单类型但由类表示的响应:

class IsResourceAvaiableDTO
{
    private bool $isAvailable;

   __construct(bool $isAvailable)
   {
       $this->isAvailable = $isAvailable;
   }

   public function isAvailable(): bool 
   {
       return $this->isAvailable;
   }
}

请注意,查询和响应 DTO 都是不可变对象 - 您不能改变它们的状态。

现在在保留域中:

class ReservationValidator
{
    
   public function validate(Reservation $reservation): ViolationsList 
   {
       $isResourceAvailableQuery = new IsResourceAvaiableQuery($reservation->getResourceId());
       $isResourceAvailableDTO = $this->messageBus->query($isResourceAvailableQuery);


      if (false === $isResourceAvailableDTO->isAvailable()) {
          // mark $reservation as invalid  
      }
   }
}

总结

这样您就有了单一的真实来源,reservation 域不需要知道您如何决定资源是否可用。此外,您可以随时更改实现,只要您不更改合约(IsResourceAvaiableQuery 和 IsResourceAvaiableDTO),它就不会影响 reservations 域。

请注意,资源可能在两个域中以不同的方式可用,您也需要进行拆分:

  • 资源 - 可供预订 - 存在并标记为可操作的项目
  • reservations - 商品尚未预订。并且您需要合并来自两个域的决策以了解全貌。