【发布时间】:2017-10-15 13:16:52
【问题描述】:
我目前正在尝试在 DDD 中编写一个允许创建、更新和删除实体的应用程序。对实体的变更必须得到另一个人的批准。应用程序还必须跟踪对实体所做的更改。简化的域模型如下所示:
应用程序有一个包含ChangeSet、Enity 和EntityHistory 的有界上下文,其中ChangeSet 是聚合根。我以这种方式设计了聚合,因为如果没有ChangeSet,则不应更改Entity,此外,ChangeSet 应与已编辑的实体一起保存在一个事务中。为此,我设计了一个单一的聚合体。
在创建新实体时设计工作非常好:
private void CreateChangeSet()
{
var repository = new ChangeSetRepository();
var entities = new List<Entity>
{
new Entity(Guid.NewGuid(), "Test1", new TagStatus(1, EntityState.Pending));
};
var changeSet = new ChangeSet("a user", "Added a new entity", DateTime.Now, ApprovalState.Submitted, entities);
repository.Insert(changeSet);
}
但是,当我尝试编辑实体时,我的设计中出现了问题:
private void EditEnity()
{
var repository = new ChangeSetRepository();
var entity = repository.GetEntityByName("Test1");
entity.AssignName("a new name");
var entities = new List<Entity>{entity};
var cs = new ChangeSet("a user", "Added a new entity", DateTime.Now, ApprovalState.Submitted, entities);
repository.Insert(cs);
}
据我所知,存储库应该只返回聚合,这意味着为了更改Entity,我必须首先搜索一个没有意义的ChangeSet。即使您仅作为聚合根执行更改,返回聚合的子实体也是一种不好的做法吗?
我在互联网上搜索了一个答案,许多人指出这种查询可以指出聚合的错误设计。这让我再次思考,如果我需要两个聚合而不是一个聚合,一个用于ChangeSet,一个包含Entity 和EntityHistory。我应该使用两个聚合而不是一个吗?如果是这样,我怎样才能在单笔交易中做到这一点?
两个聚合的进一步指示是用户界面要求,例如“用户希望查看实体的更改历史记录”或“显示视图中的所有实体”。一方面这表明两个聚合,另一方面我觉得ChangeSet 和Entities 应该真正属于一起。
总结一下我的问题:
- 我应该在设计中使用一种还是两种聚合?
- 如果是一个聚合:即使您仅通过聚合根执行更改,返回聚合的子实体也是一种不好的做法吗?
- 如果有两个聚合:如何在一个事务中保存
ChangeSet和关联的Entities?
【问题讨论】:
-
你为什么不看看事件溯源,事件将成为你的历史,你不需要做任何额外的事情?
-
我会听从 Alessandro Santini 的建议,尝试一下 event sourcing。我感觉变更集不适合模型,事件溯源可以解决这个问题。
-
这里的问题是版本控制问题更具技术性和通用性。因此,将其建模为领域模型的一部分可能是矫枉过正。您还可以考虑数据库版本控制模型,例如 RavenDb 有一个版本控制包,可以保留一定数量的文档版本。这也是一种有效的方法。
标签: c# domain-driven-design aggregate