在单个事务中修改两个聚合(DDD)

时间:2017-05-24 07:51:10

标签: c# domain-driven-design

我有这个简单的域模型。用户执行具有持续时间和距离的活动。对于每个活动,可以分配使用的齿轮项目(用于跑步的鞋子)。分配后,持续时间和距离将添加到齿轮项目的总计中。

Activity和GearItem都是聚合根。当我将项目分配给活动时,我需要将项目ID添加到活动中并更新项目上的总计。

如何协调此操作?

  1. 直接从GearItem.AssignUsage()
  2. 致电Activity.AddItem()
  3. 在单个交易中的应用服务中执行,在GearItem.AssignUsage()之后调用Activity.AddItem()(它违反了每个AR的一个交易规则)。
  4. 使用域名活动(Udi Dahan的静态类) - 但是如何以及在何处避免单一交易问题?
  5. 或其他更好的东西?
  6. GearItem AR,包含来自活动的总计:

    public class GearItem: Entity<GearItem>
    {
      ...
     public Usage AssignedUsage { get; private set; };
    
      public void AssignUsage(Activity activity)
      {
        AssignedUsage = new Usage(AssignedUsage.Time + activity.Time, 
        AssignedUsage.Distance + activity.Distance, AssignedUsage.Uses + 1);
      }    
    }
    

    活动AR,包含已分配的GearItems的ID:

    public class Activity: Entity<Activity>
    {
      ...
      private List<int> items = new List<int>(); 
      public TimeSpan Time { get; }
      public double Distance { get; }
    
      public bool AddItem(GearItem item)
      {
        if (items.Contains(item.Id)) return false;
    
        items.Add(item.Id);
        return true;
      }
    }
    

    使用价值对象:

    public class Usage: ValueObject<Usage>
    {
      ...
      public TimeSpan Time { get; private set; }
      public double Distance { get; private set; }
      public int Uses { get; private set; }
    }
    

1 个答案:

答案 0 :(得分:2)

您应该抵制在单个事务中修改两个聚合的诱惑。有两条规则可以帮助您不要这样做:

  1. 仅根据其ID引用其他聚合
  2. 使用边界外的最终一致性
  3. 因此,为了更新AssignedUsage的{​​{1}},您将使用最终的一致性。

    您可以使用域事件或定期计算所有GearItem的使用情况来执行此操作。

    使用域名活动:

    在为活动分配GearItems后,您发布GearItem个活动,该活动由在新交易中调用GearItemAssignedToActivity的{​​{1}}抓住(首先加载GearItem使用其来自存储库的ID。)

    定期更新GearItem的使用

    在cron /计划任务/您重置每个Process manager的使用情况的任何内容中,然后加载每个GearItem.AssignUsage(Time,Distance)并为每个已分配的GearItem加载并致电Activity。< / p>

    另外,我建议您解耦两个聚合并且不传递对方法调用的引用。例如,您不应将整个活动聚合传递给GearItem.AssignUsage(Time,Distance)方法,而只传递所需的属性。这样,GearItem一定不能知道或取决于整个GearItem.AssignUsage