(这可能是重复的,但我在其他问题中找不到任何令人满意的答案。)
我需要一些关于如何将聚合根的子实体的删除传播到数据库层的指导。我已经/阅读了几种不同的方法来处理它:
保留被移除的孩子我分开收集。让存储库在保存时删除集合中的所有项目。
直接调用某种数据映射层或存储库。
执行存储库/ UoW可能提供/收听的某种类型的委托/事件。
什么都不做。让工作单元或存储库将整个聚合根与“非脏”副本进行比较。
值得注意的是,我真正的聚合根实现包含多个可能被删除的子集合。
为了清楚起见,这是一个简单的例子:
public class AggregateRoot
{
private List<ChildEntity> _children;
public IEnumerable<ChildEntity> Children
{
get
{
return _children;
}
}
public void RemoveChild(ChildEntity child)
{
_children.Remove(child);
// What to do here?
// Option 1: Keep removed children i separate collection. Let
// the repository remove them on save.
RemovedChildren.Add(child);
// Option 2: Call some kind of data mapping layer or repository directly
_childRepository.Delete(child);
// Option 3: Execute some type of delegate/event that a repository
// may provide/listen to.
_onChildRemoved(this, child);
// Option 4: Do nothing. Let the Unit of work or repository compare
// the whole aggregate root with a "non-dirty" copy.
}
}
我认为选项1和2不是很优雅。
选项4很好,因为我不必在聚合根中实现任何逻辑,但由于我的实际实现非常大,我将不得不比较很多属性和集合。
我倾向于支持选项3.问题在于它可能需要很多委托/事件。也许我可以实现一个通用的委托/事件。
这一定是一个相当常见的问题,但我很少见到这个例子。你会如何处理这个问题?
答案 0 :(得分:1)
选项1: 我想在你的业务中没有像RemovedChildren这样的概念,所以聚合在我看来不应该包含这样的集合。请注意,有时可能会出现像ChildrenHistory,HistoricalChildren
这样的概念选项2: 我已经多次读过聚合不应该使用存储库,聚合不应该注入任何服务。但是,您可以使用双重调度模式并提供方法签名,如:
public void RemoveChild(ChildEntity child, IChildRepository repository)
{
// check conditions, business logic etc
// remove using repository
然而,它强制调用者提供存储库,在某些情况下可能很难实现
选项3: 我以前没有为此目的使用过事件。但对我来说似乎还不错。您可以使用事件总线通知感兴趣的存储库有关实体删除的信息。
选项4: 在我看来,这是最好的方式,UoW应该处理项目删除。您可以通过在其上设置某种标记或将其ParentId设置为0(这就是EF在幕后所做的那样)将您的实体标记为已删除。在提交时,您可以检查加载到内存中的所有对象,并删除那些标记为删除的对象。这迫使我们在域层中编写最少的基础设施代码,但是我们需要在UoW中编写额外的代码来删除孤儿。