问题描述
我是 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 - 商品尚未预订。并且您需要合并来自两个域的决策以了解全貌。