Domain driven design DDD:聚合根

Domain driven design DDD:聚合根,domain-driven-design,aggregate,aggregateroot,aggregates,Domain Driven Design,Aggregate,Aggregateroot,Aggregates,我需要帮助找到我的聚合根和边界 我有3个实体:计划、计划角色和计划培训。每个计划可以包括许多计划角色和计划培训 解决方案1:起初我认为计划是聚合根,因为PlannedRole和PlannedTraining在计划的上下文中没有意义。他们总是在计划之内。此外,我们有一条业务规则,规定每个计划最多可以有3个计划角色和5个计划培训。所以我认为通过将计划指定为聚合根,我可以强制执行这个不变量 但是,我们有一个搜索页面,用户在其中搜索计划。结果显示了计划本身的一些属性(并且没有计划的角色或计划的培训)。我

我需要帮助找到我的聚合根和边界

我有3个实体:计划、计划角色和计划培训。每个计划可以包括许多计划角色和计划培训

解决方案1:起初我认为计划是聚合根,因为PlannedRole和PlannedTraining在计划的上下文中没有意义。他们总是在计划之内。此外,我们有一条业务规则,规定每个计划最多可以有3个计划角色和5个计划培训。所以我认为通过将计划指定为聚合根,我可以强制执行这个不变量

但是,我们有一个搜索页面,用户在其中搜索计划。结果显示了计划本身的一些属性(并且没有计划的角色或计划的培训)。我想如果我必须加载整个聚合,它会有很多开销。有近3000个计划,每个计划可能有几个孩子。将所有这些对象加载到一起,然后在搜索页面中忽略PlannedRoles和PlannedTrainings对我来说没有意义

解决方案2:我刚刚意识到用户需要另外两个搜索页面,在那里他们可以搜索计划的角色或计划的培训。这让我意识到他们试图独立访问这些对象,并且“脱离”计划的上下文。所以我认为我的初始设计是错误的,这就是我提出这个解决方案的原因。所以,我认为这里有3个聚合,每个实体1个

这种方法使我能够独立地搜索每个实体,还解决了解决方案1中的性能问题。但是,使用这种方法,我无法强制执行前面提到的不变量

还有一个不变量表示,只有当计划处于某一状态时,才能对其进行更改。所以,我不能在一个不处于该状态的计划中添加任何PlannedRoles或PlannedTraining。同样,我不能用第二种方法强制执行这个不变量

如有任何建议,将不胜感激

干杯,
莫斯

在设计我的模型时,我也遇到了类似的问题,我问了这个问题,我认为这个问题可能会对你有所帮助,特别是关于你的第一点

当涉及到搜索时,我不使用“模型”,而是使用返回“摘要”对象的专门搜索存储库。。。i、 e.“计划摘要”。这些对象只不过是信息对象(可以被认为更像报告),并没有在事务意义上使用——我甚至没有在我的模型类库中定义它们。通过创建这些专用存储库和类型,我可以实现高性能的搜索查询,这些查询可以包含分组数据(如PlannedTraining计数),而无需在内存中加载聚合的所有关联。一旦用户在UI中选择了其中一个摘要对象,我就可以使用该ID获取实际的模型对象并执行事务操作和提交更改

因此,对于您的情况,我将为所有三个实体提供这些专门的搜索存储库,当用户希望对其中一个实体执行操作时,您总是获取它所属的计划集合

通过这种方式,您可以进行性能搜索,同时仍然使用所需的不变量维护单个聚合

编辑-示例:

好的,我想实现是主观的,但这就是我在应用程序中处理它的方式,以“TeamMember”聚合为例。用C#编写的示例。我有两个类库:

  • 模型
  • 报告
模型库包含聚合类,并强制执行所有不变量,报告库包含以下简单类:

public class TeamMemberSummary
{
    public string FirstName { get; set; }

    public string Surname { get; set; }

    public DateTime DateOfBirth { get; set; }

    public bool IsAvailable { get; set; }

    public string MainProductExpertise { get; set; }

    public int ExperienceRating { get; set; }
}
报表库还包含以下界面:

public interface ITeamMemberSummaryRepository : IReportRepository<TeamMemberSummary>
{

}
公共接口ITeamMemberSummaryRepository:IReportRepository
{
}
这是应用层(在我的例子中,恰好是WCF服务)将使用的接口,并将通过我的IoC容器(Unity)解决实现问题。IReportRepository与基本ReportRepositoryBase一样位于Infrastructure.Interface库中。所以我的系统中有两种不同类型的存储库-聚合存储库和报告存储库

然后在另一个库Repositories.Sql中,我实现了:

public class TeamMemberSummaryRepository : ITeamMemberSummaryRepository
{
    public IList<TeamMemberSummary> FindAll<TCriteria>(TCriteria criteria) where TCriteria : ICriteria
    {
        //Write SQL code here

        return new List<TeamMemberSummary>();
    }

    public void Initialise()
    {

    }
}
公共类TeamMemberSummaryRepository:ITeamMemberSummaryRepository
{
公共IList FindAll(TCriteria标准),其中TCriteria:ICriteria
{
//在此处编写SQL代码
返回新列表();
}
公开无效初始化()
{
}
}
因此,在我的应用层:

    public IList<TeamMemberSummary> FindTeamMembers(TeamMemberCriteria criteria)
    {
        ITeamMemberSummaryRepository repository 
            = RepositoryFactory.GetRepository<ITeamMemberSummaryRepository>();

        return repository.FindAll(criteria);

    }
public IList FindTeamMembers(TeamMemberCriteria)
{
ITeamMemberSummary存储库
=RepositoryFactory.GetRepository();
返回repository.FindAll(标准);
}
然后,在客户端中,用户可以选择这些对象中的一个,并在应用层中对其中一个对象执行操作,例如:

    public void ChangeTeamMembersExperienceRating(Guid teamMemberID, int newExperienceRating)
    {
        ITeamMemberRepository repository
            = RepositoryFactory.GetRepository<ITeamMemberRepository>();

        using(IUnitOfWork unitOfWork = UnitOfWorkFactory.CreateUnitOfWork())
        {
            TeamMember teamMember = repository.GetByID(teamMemberID);

            teamMember.ChangeExperienceRating(newExperienceRating);

            repository.Save(teamMember);
        }
    }
public void ChangeTeamMembersExperienceRating(Guid teamMemberID,int newExperienceRating)
{
ITeamMemberRepository
=RepositoryFactory.GetRepository();
使用(IUnitOfWork unitOfWork=UnitOfWorkFactory.CreateUnitOfWork())
{
TeamMember TeamMember=repository.GetByID(teamMemberID);
团队成员。变更体验培训(新体验培训);
保存(团队成员);
}
}

这里真正的问题是SRP违规。应用程序的输入部分与输出部分发生冲突

坚持第一个解决方案(计划==聚合根)。人为地将实体(甚至是价值对象)提升为聚合根会扭曲整个域模型并破坏一切


您可能希望查看所谓的(命令查询责任分离)体系结构,它非常适合解决这个特定问题。作者:马克·尼霍夫。这是一个不错的列表。

这是t