DDD:聚合根

Mos*_*osh 6 domain-driven-design aggregate aggregateroot aggregates

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

我有3个实体:Plan,PlannedRole和PlannedTraining.每个计划都可以包含许多PlannedRoles和PlannedTraining.

解决方案1:起初我认为Plan是聚合根,因为PlannedRole和PlannedTraining在计划的上下文中没有意义.他们总是在计划之内.此外,我们有一个业务规则,即每个计划最多可以有3个PlannedRoles和5个PlannedTraining.所以我认为通过提名计划作为聚合根,我可以强制执行这个不变量.

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

解决方案2:我刚刚意识到用户还需要2个搜索页面,他们可以搜索计划角色或计划培训.这让我意识到他们正试图独立地访问这些对象并"脱离"Plan的背景.所以我认为我的初始设计错了,这就是我想出这个解决方案的方法.所以,我认为这里有3个聚合,每个实体有1个聚合.

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

还有另一个不变量,即只有在具有特定状态时才能更改计划.因此,我不能将任何PlannedRoles或PlannedTrainings添加到不处于该状态的计划中.同样,我不能用第二种方法强制执行这种不变量.

任何建议将不胜感激.

干杯,莫什

Dav*_*ers 9

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

DDD - 如何实现高性能的搜索存储库.

在搜索时我不使用'模型',而是我有专门的搜索存储库返回'Summary'对象...即'PlanSummary'.这些只不过是信息对象(可以被认为更像是报告)而且不是在事务意义上使用 - 我甚至没有在我的模型类库中定义它们.通过创建这些专用存储库和类型,我可以实现可以包含分组数据(例如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; }
}
Run Code Online (Sandbox Code Playgroud)

报告库还包含以下界面:

public interface ITeamMemberSummaryRepository : IReportRepository<TeamMemberSummary>
{

}
Run Code Online (Sandbox Code Playgroud)

这是应用程序层(在我的情况下恰好是WCF服务)将使用的接口,并将通过我的IoC容器(Unity)解析实现.IReportRepository存在于Infrastructure.Interface库中,基本ReportRepositoryBase也是如此.所以我的系统中有两种不同类型的存储库 - 聚合存储库和报告存储库......

然后在另一个库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()
    {

    }
}
Run Code Online (Sandbox Code Playgroud)

那么,在我的应用层:

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

        return repository.FindAll(criteria);

    }
Run Code Online (Sandbox Code Playgroud)

然后在客户端中,用户可以选择其中一个对象,并对应用程序层中的一个执行操作,例如:

    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);
        }
    }
Run Code Online (Sandbox Code Playgroud)