【发布时间】:2018-01-11 11:24:37
【问题描述】:
我们正在启动一个新项目,并且热衷于应用 DDD 原则。该项目使用 dotnet core,EF core 为 SQL Server 提供持久性。
域的初始视图 我将使用任务跟踪器的示例来说明我们的问题和挑战,因为这将遵循类似的结构。
一开始我们了解以下内容:-
- 我们有一个项目
- 用户可以关联到项目
- 一个项目有工作流
- 工作流有任务
- 用户可以针对任务发布评论
- 用户能够更改任务的状态(进行中、完成等)
- 一个项目,以及相关的工作组和任务最初是从一个模板创建的
最初的设计是一个大型集群聚合,其中 Project 是根聚合,包含 ProjectUsers 和 Workstreams 的集合, Workstreams 有一系列 Tasks 等
这种方法显然会导致许多争用和性能问题,因为必须为该聚合中的任何更改加载整个 Project 聚合。
无论对错,我们的下一个修订是从聚合中拆分出Comments,并使用Comment 作为根形成一个新的聚合。这样做的动机是,企业设想每个任务都会收到大量评论。
由于每个 Comment 都与 Task 相关,因此 Comment 需要将外键保留回 Task。但是,根据您只能通过其根引用另一个聚合的原则,这是不可能的。为了克服这个问题,我们将 Task 分解为另一个聚合。这似乎也满足了任务可以由不同的人完成并再次减少争用的需求。
然后我们遇到了同样的问题,从 Task 到 Task 所属的 Workstream 的引用导致我们创建一个新的 Task strong>Workstream 与 Task 中的外键聚合回 Workstream。
结果是:-
- Project 聚合仅包含分配给该项目的用户列表
- Workstream 聚合,其中包含 Project 的外键
- Task 聚合,其中包含 Project 的外键
- Comments 聚合,其中包含返回 Task 的外键
Project 有一个创建Workstream 新实例的方法,允许我们设置外键。 IE。稍微简化的版本
public class Project()
{
string _name { get; private set;}
public Project(Name)
{
_name = Name;
}
public Workstream CreateWorkstream(string name)
{
return new Workstream(name, Id);
}
....+ Methods for managing user assignment to the project
}
以类似的方式,Workstream 有一个方法来创建一个任务
public class Workstream()
{
string _name { get; private set;}
public int ProjectId { get; private set; }
public Workstream(Name, Id)
{
_name = Name;
_projectId = Id;
}
public Task CreateTask(string name)
{
return new Task(name, Id);
}
private readonly List<Task> _activities = new List<Task>();
public IEnumerable<Task> Activities => _activities.AsReadOnly();
}
- 添加了 Activities 属性,纯粹是为了在使用实体构建读取模型时支持导航。
团队对这种方法感到不舒服,感觉有些不对劲。主要问题是:-
- 感觉创建项目逻辑上应该是创建项目,向项目添加一个或多个工作流,向工作流添加任务,然后让EF处理持久化该对象结构。
- 令人不安的是,必须首先创建项目,并且开发人员需要确保它被持久化,以便获得一个 Id,以便在调用创建模板的方法时做好准备,该方法依赖于外来的 Id钥匙。是否可以将此责任推给域服务中的方法CreateProjectFromTemplate() 以协调各个存储库的单独对象的创建和持久性?
- 即使在正确的位置创建新工作流的方法是否正确?
- 实体用于形成用于创建读取模型的查询(由导航属性支持)。也许担心的是对象结构受到我们需要如何以只读方式呈现数据的影响
我们现在正处于兜圈子的地步,确实可以使用一些建议来给我们一些方向。
【问题讨论】:
-
您的问题似乎非常多样化,其中一些问题已经在 SO stackoverflow.com/questions/25742703/… stackoverflow.com/questions/32082479/… 上多次讨论过
-
关于域建模建议,至少只要我们不知道应用程序的用例、不变量和协作/事务负载,它就接近于“主要基于意见”。
标签: domain-driven-design aggregate