【问题标题】:How to control invariants through the aggregate root如何通过聚合根控制不变量
【发布时间】:2023-03-05 08:13:01
【问题描述】:

如果我有两个类[Shift,ShiftDetails],其中[Shift] 是聚合根。基于特定字段,我想限制ShiftDetails 的实例数。


我创建了一个ShiftDetailsView 类,然后在Shift 中创建两个方法 聚合根来控制不变量:

  • public IEnumerable ConstructShift();//基于 NumberOfSuccessions 字段

此方法应基于该字段创建初始化ShiftDetailsView 的数量,然后将它们添加到列表中并将结果作为 IEnumerable 返回给用户(开发者)。

那么用户应该在填充上一个方法返回的初始IEnumerable后调用CompleteShift

  • 公共列表 CompleteShift(IEnumerable shiftDetailsViews)

我做了两个单独的步骤来通过聚合根来控制孩子的数量,我认为有更好的方法来保证整个事情的(ACID)。


基于cmets,问题需要进一步澄清,因为我试图简化问题,所以我犯了一个错误并改变了真正的问题。因为当我们处理领域问题时,我们必须准确并澄清确切的问题。所以我会尝试更详细地解释它。

我有以下两个聚合:

1。第一聚合

1-WorkingSystem:(聚合根)

    private readonly ICollection<WorkingTime> _assignedWorkingTimes;

    public string Name { get; private set; }
    public short NumberOfSuccessions { get; private set; }
    public Week WeekStart { get; private set; }
    public bool IsActive { get; private set; }
    public bool IsDefault { get; private set; }
    public short NumberOfWeekends { get; private set; }
    public virtual ICollection<WorkingTime> AssignedWorkingTimes { get => _assignedWorkingTimes; }

例子:

 Id |Name     | NumberOfSuccessions|WeekStart|IsActive|NumberOfWeekends 
  1 |Employees|       2            |Sunday   |  1     |   2
  2 |Lecturers|       1            |Saturday |  1     |   1

2-工作时间:

public string Name { get; set; }
public short NumberOfHours { get; set; }
public int WorkingSystemId { get; private set; }

例子:

Id|Name   |  NumberOfWorkingHours  | WorkingSystemId 
 1|Summer |  8                     | 1
 2|Winter |  6                     | 1
 3|General|  8                     | 2

2。第二个聚合

3-Shift(聚合根)。

private readonly List<ShiftDetail> _assignedShiftDetails;
public string Name { get; set; }
public ShiftType ShiftType { get; set; }
public int WorkingSystemId { get; set; }
public virtual WorkingSystem WorkingSystem { get; set; }
public virtual IEnumerable<ShiftDetail> AssignedShiftDetails { get => _assignedShiftDetails; }

例子:

Id|Name            |ShiftType | WorkingSystemId 
1 |restaurant-shift|Morning   |  1

4- ShiftDetails:

public Guid ShiftId { get; private set; }
public int WorkingTimeId { get; set; }
public DateTimeRange ShiftTimeRange { get; set; }
public virtual WorkingTime WorkingTime { get; set; }

例子:

 ShiftId = 1|WorkingTimeId = 1|ShiftStart = 8:00|ShiftEnd = 16:00
 ShiftId = 1|WorkingTimeId = 2|ShiftStart = 9:00|ShiftEnd = 15:00

现在我想根据 NumberOfSuccessions 为每个 WorkingSystem 限制详细信息的数量!并且因为我可以访问聚合根 Shift 中的 WorkingSystemId,所以我可以访问此信息。

你能帮我根据聚合根中的一个字段来控制实例的数量吗?

【问题讨论】:

  • 好吧,您可以简单地添加一个 AsignShiftDetail(detail) 方法,该方法在调用的次数超过 NumberOfDetails 时会引发异常。但选择最佳解决方案取决于您的领域。你在建模什么?这些类试图解决什么问题?
  • ShiftDetails 和 ShiftDetailsView 是一样的吗?因为我觉得如果正确的话可能会产生误导,请修复它们
  • @amirhamini 不,它是域类的较小版本
  • 为什么需要ConstructShift()?您是否必须根据当前的shift 实例设置某种要填充的模板,或者只是让客户知道必须提供多少班次?
  • @plalx:这是一个很好的问题。根据我所阅读的内容,我的示例中的聚合根 (shift) 应该控制我的示例中的不变量 (the number of shift details) 以使系统处于一致状态。所以我为客户端创建了这个方法来调用它,它会返回IEnumerable of shiftDetailsViews它由正确数量的空shiftDetailsViews初始化然后客户端可以通过循环填充这个初始模板IEnumerable并设置它们值然后在最后调用CompleteShift(IEnumerable shiftDetailsViews)

标签: c# architecture domain-driven-design ienumerable aggregateroot


【解决方案1】:

你能帮我根据聚合根中的一个字段来控制实例的数量吗?

这两种方法的使用仅限于创建新班次。因为我是 DDD 的新手,所以我认为我没有以正确的方式约束不变量

哦,我明白了——您特别担心创建聚合的事务中会发生什么。

Eric Evans(第 6 章)描述的通常模式是域模型将公开一个工厂,该工厂知道如何从应用程序中获取数据并返回一个新的聚合实例。 p>

在该工厂中,您正在创建构成聚合初始状态的所有值。这些值了解它们自己的约束(“我是Amount,所以我需要一个大于或等于零的整数”;“我是Money,所以我需要一个非空Amount和一个非空@ 987654326@).

工厂从应用程序接收数据,构造值图,最后调用具有当前状态的聚合的构造函数。

在模型中有负责验证已传入数据的值是非常正常的事情,这可能包括有一个值来验证其他两个值是否一致。

因此,例如,您的工厂可能会采用 NumberOfDetailsList&lt;ShiftDetails&gt;,并从它们生成 VerifiedDetails 类型,最终将由它们构造聚合。

Scott Wlaschin's book Domain Modeling Made Functional 非常适合阅读此方法。

【讨论】:

  • 感谢您的回复,但是这里有个误区,用户在将数据保存到DB后无法更新root。所以没有这样的。这两种方法的使用仅用于创建新班次。因为我是DDD 的新手,所以我认为我没有以正确的方式限制invariants,所以我寻求帮助。如何通过root控制invariants
  • @AnynameDonotcare “这两种方法的使用仅用于创建新班次” - 如果这一切都发生在对象上,我不明白你为什么要编写单独的方法实例化时间。 Complete() 表明这是一个多步骤、长期有效的转换,而不仅仅是一个简单的对象实例化。
  • 谢谢,我有一些问题。 1-为什么我应该使用Factory 而不是Constructor 来处理我的变体? 2-这里Factory的含义是static factory method in the same class还是单独的abstract factory class? 3-我应该在我的聚合根目录中使用工厂方法,它会负责创建整个聚合吗?或者聚合中的每个孩子都有一个工厂方法,是什么让根中的那个不同的是控制不同的变体?
  • 4-根据您的回答,您建议将List&lt;ShiftDetails&gt;传递给根中的工厂方法!客户端代码(应用程序代码)可以直接在聚合中处理孩子吗?因为我为此创建了ShiftDetailsview。 5-请问哪个与DDD 对齐,将ShiftDetails 视为值对象或实体? 6-对不起,我的最后一个问题,请问VerifiedDetails type 在这种情况下是什么意思?
  • 这是一个非常重要的“细节” :) 如果不变量存在于聚合 A 中,您应该让聚合 A 强制执行它,而不是正在构建的聚合 B...
【解决方案2】:

我将向 Shift 类添加一个名为 AssignedShiftDetail(ShiftDetail shiftDetail) 的方法,并将在此方法中实现任何相关逻辑。

【讨论】:

    猜你喜欢
    • 2017-10-26
    • 2017-07-27
    • 2022-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-31
    相关资源
    最近更新 更多