DDD Repo-repo.findByChildId(id)和repo.transferChild(to,from,child)

jam01

我最近开始研究DDD,并已将旧的个人项目重构为这种模式。我已经接近埃文斯蓝皮书的一半了,似乎无法在那里或在任何地方在线找到答案。

基本上,我的应用程序是一个库存跟踪器。库存将包含项目的集合,项目是库存之间的可转让实体。库存将具有类似的方法transferIn() transferOut()这些方法将包含一些验证逻辑,即检查库存是否尚未满或物品是否处于可转让状态。这些限制使我相信库存是总根,而该项目是一个实体。

1)在某个时候,如果用户请求其库存的特定物料实体,我希望有一个inventoryRepo.findByItemId(id),该实体将返回当前拥有该物料的库存。这样我可以:

2)通过服务执行以下操作:

boolean requestItemTransfer(destInvId, itemId){
    Inv from = invRepo.findByItemId(itemId);
    Inv to = invRepo.findById(destInvId);
    from.transferOut(itemId);
    to.transferIn(from.getItem(itemId));
    return invRepo.transferChild(to, item); //Edited
}

基本上是在清单类(富域模型)中编写验证逻辑,如果没有异常,则使用repo.transfer()方法保存更改。

我会违反DDD吗?有更好的选择吗?

根据我的阅读和理解,这只是非常规的,这似乎是正确的。我发现的每个示例都显示只能存在于1个根实例中的实体。也有银行帐户转账示例,但这些示例处理的是作为价值对象的金额,并具有转账存储库,因为转账将在该特定情况下记录,而不仅仅是在我的情况下。

编辑:用例如下:

1)用户请求清单和物品清单。

2)用户从1个库存中选择1个或多个项目,并请求将其发送到另一个库存。这是我的TransferService进入的位置,可以协调指定清单中的txIn和txOut,并通过存储库保留这些更改。也许应该是基础设施服务?这是我不清楚的一件事。

3)用户预定义了一组他希望能够转移到这些物品当前所在库存的物品清单。TransferService将找到这些物品当前在哪里,并与用例2一样协调其余物品。

EDIT2:关于repo.transfer这实际上是一个约束/优化吗?从数据方面来说,据我所知,它只是查找项目并更改其指向的库存ID。这是因为项目不能一次在2个库存中。因此,而不是repo.update(fromInvInNewState)repo.update(toInvInNewState)存在repo.moveChild(toInv, child),因为我们不希望重写库存的整个状态(所有的物品都没有移动,并且它具有在任何时候,因为其状态的剩余部分从项目得到的),只是移动一些物品。

恩贡扎莱兹拜尔

首先,我想回顾一下您的方案定义的域模型。

您说要使用下一个规范来构建库存跟踪器:

  • 用户有库存。
  • 库存由项目组成。
  • 用户可以将物品从一个库存转移到另一个库存。我猜这两个库存都属于用户,正如您所说的:“用户请求其库存和物品清单。用户从1个库存中选择1个或多个物品,并要求将其发送到另一个库存...”

另一方面,您指出的不变式是:

  • 仅当库存B尚未满时,才可以将其从已经存在的库存(InventoryA)转移到另一个库存(InventoryB)。我想如果万一无法转移物料,则应将其保存在库存A中。
  • 如果我理解得很好,则用户可以在其存储库之间转移其项目。

就像是:

class TransferItemService {
    public function execute(TransferItemRequest request)
    {
        user = userRepository.findOfId(request.userId());
        user.transferItem(request.itemId(), request.fromInventoryId(), request.toInventoryId()); //Checks invariant -> the given Item is in one of his Inventories, the destination Inventory is owned by him, the destination Inventory is not full and finally transfers the Item
        userRepository.save(user);
    }
}

现在,为了定义汇总根,我需要知道我的业务是否可以处理最终的一致性。也就是说,如果必须自动完成一项(仅一个请求)的移动,否则可能会花费一些时间(一个以上的请求)。

没有最终的一致性

万一业务说这里不允许最终一致性,那么如果您要确保您的域保持一致并与不变式保持一致,则用户将是唯一的AggregateRoot,因为他是其清单之间的联系。在这种情况下,由于加载所有清单及其物料,您可能会遇到性能问题。

最终一致性

在情况下,您可以用最终consitency去,你可以有一个聚合根:UserInventoryItem因此,使用前面的代码来建模传输项目的用例:

class TransferItemService {
    public function execute(TransferItemRequest request)
    {
        user = userRepository.findOfId(request.userId());
        user.transferItem(request.itemId(), request.fromInventoryId(), request.toInventoryId()); //Checks invariant -> the given Item is in one of his Inventories, the destination Inventory is owned by him, the destination Inventory is not full and finally transfers the Item
        userRepository.save(user);
    }
}

在这种情况下,该transferItem方法将如下所示:

class User {
  private string id;
  private List<UserInventory> inventories;

  public function transferItem(itemId, fromInventoryId, toInventoryId)
  {
    fromUserInventory = this.inventories.get(fromInventoryId);
    if(!fromUserInventory) throw new InventoryNotBelongToUser(fromInventoryId, this.id);

    toUserInventory = this.inventories.get(toInventoryId);
    if(!toUserInventory) throw new InventoryNotBelongToUser(toInventoryId, this.id);

    toUserInventory.addItem(itemId);
    fromUserInventory.deletetItem(itemId);
  }
}

class UserInventory {
  private String identifier;
  private int capacity;

  public function deleteItem(userId, itemId)
  {
      this.capacity--;
      DomainEventPublisher.publish(new ItemWasDeleted(this.identifier, itemId));

  }

  public function addItem(userId, itemId)
  {
    if(this.capacity >= MAX_CAPACITY) {
      throw new InventoryCapacityAlreadyFull(this.identifier);
    }
    this.capacity++;

    DomainEventPublisher.publish(new ItemWasAdded(this.identifier, itemId));
  }
}

请注意,这UserInventory不是Inventory聚合根,它只是一个具有标识符引用和实际容量的VO Inventory

现在,您可以拥有一个可以异步更新每个清单的侦听器:

class ItemWasRemovedListener()
{
  public function handleEvent(event)
  {
    removeItemFromInventoryService.execute(event.inventoryId(), event.itemId());
  }
}

class ItemWasAddedListener()
{
  public function handleEvent(event)
  {
    addItemToInventoryService.execute(event.inventoryId(), event.itemId());
  }
}

除非我犯了一个错误,否则我认为我们已经满足了所有不变量,我们只为每个请求修改了一个聚合根,并且不需要加载所有项目就可以对清单执行操作。

如果您发现有问题,请告诉我:D。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章