我有一个Contract
实体,其中包含DateRange
(dateFrom,dateTo)属性和Sales
个集合。
每个Sale
也有一个DateRange
属性,必须位于Contract
DateRange
的边界内。
更改Sale
日期时执行上述不变量的正确方法是什么?
public class Contract : Entity
{
public DateRange Dates { get; private set; }
public ICollection<Sale> Sales { get; private set; }
}
public class Sale : Entity
{
public DateRange Dates { get; private set; }
public void ChangeDates(DateRange dates)
{
Dates = dates;
}
}
修改
Contract
日期可以随时更改,因此应相应修改每个Sale
。
答案 0 :(得分:3)
解释您的要求,Contract
是聚合根,Sale
是Contract
聚合中的实体。由于要求任何销售日期必须在一组合同日期内,因此对销售日期的任何更改必须由合同管理,因此它可以首先检查合同日期。
为此,您需要Contract
上的方法,例如:
public void ChangeSaleDate(long SaleId, DateRange dates)
{
if (this.Dates.Surround(dates))
{
var sale = this.Sales.First(s => s.Id == SaleId);
sale.ChangeDates(dates);
}
else
{
throw new ArgumentException("New Sale dates must be between ...", "dates");
}
}
这假设您使用SaleId
或其他方式识别合同中的销售,并且您已在Surround
上实施了DateRange
方法来支持此检查。
根据您的项目结构,您还可以将ChangeDates
上的Sale
方法标记为internal
,以确保您不会意外地从您的应用服务中调用它。
根据您的评论,这是正确的,这种机制可以导致聚合根(Contract
)上的大量方法,因为它强制执行适用于所有&#39;所有&#39的不变量;在合同中销售。因此,像这样的情况可能会提示挑战要求......
DDD有助于最终的一致性和#39;聚合之间 - 作为聚合定义一致性边界,如果要定义跨越边界的规则,则必须接受该规则可能始终应用。
另一种实现方式是使Sale
成为自己的聚合。在这种情况下,您在ICollection<Sale>
上没有Contract
属性 - 而是在ContractId
上只有Sale
属性,每次促销都会获得自己的全球唯一标识符。
然而,这种技术的可行性取决于合同日期是否允许改变,以及当它们做什么时......应该说明:
要更改促销的日期,您可以使用ContractRepository
获取Contract
,使用SaleRepository
获取Sale
,也可以将合同传递给Sale
上的日期更改方法:
public void ChangeDate(Contract contract, DateRange dates)
{
if (contract.Id != this.ContractId)
throw new ArgumentException("wrong contract", "contract");
if (!contract.AreSaleDatesValid(dates))
throw new ArgumentException("wrong dates", "dates");
this.Dates = dates;
}
此处的风险,因为您的合约和销售在交易上不一致,取决于合约日期是否会发生变化。
如果没有,那么这种方法简单易行,并确保您可以直接访问销售。
但是,如果他们可以,则风险是合约日期可能会同时更改 您正在更改销售日期,因此您的规则将会暂时中断。 / p>
但是,这是域事件可能有所帮助的地方。如果您的Sale.ChangeDate
方法发布了事件SaleDatesChanged
,并且您在新事务中异步处理事件,则处理程序可以检查销售日期是否仍然对合同有效。
接下来会发生什么情况取决于您的业务需求 - 提醒人工审核,或自动更改销售日期以适应新合约日期?
同样,Contract.ChangeDate
方法会发布ContractDatesChanged
,此处理程序将检查所有销售是否在合同日期内,并再次发出警报或调整。
这是最终的一致性&#39;根据DDD要求 - 所有销售必须在合同日期内的规则将最终得到满足。
这就是为什么我说'挑战'&#39;要求 - 如果允许销售日期在这些情况下超出合同日期并以业务适当的方式处理它真的更好,那么您已经挑战了您的要求并形成了更深入的洞察力进入域名。