domain-driven-design - DDD : Aggregate Roots

标签 domain-driven-design aggregate aggregateroot aggregates

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

我有 3 个实体:Plan、PlannedRole 和 PlannedTraining。每个计划可以包括许多 PlannedRoles 和 PlannedTrainings。

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

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

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

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

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

任何建议将不胜感激。

干杯,
莫什

最佳答案

在设计我的模型时,我遇到了类似的问题,并提出了这个我认为可能对你有帮助的问题,尤其是关于你的第一点。

DDD - How to implement high-performing repositories for searching .

在搜索方面,我不使用“模型”,而是使用专门的搜索存储库返回“摘要”对象......即“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; }
    }
    

    报告库还包含以下接口(interface):
    public interface ITeamMemberSummaryRepository : IReportRepository<TeamMemberSummary>
    {
    
    }
    

    这是应用程序层(在我的例子中恰好是 WCF 服务)将使用的接口(interface),并将通过我的 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()
        {
    
        }
    }
    

    那么,在我的应用层:
        public IList<TeamMemberSummary> FindTeamMembers(TeamMemberCriteria criteria)
        {
            ITeamMemberSummaryRepository repository 
                = RepositoryFactory.GetRepository<ITeamMemberSummaryRepository>();
    
            return repository.FindAll(criteria);
    
        }
    

    然后在客户端中,用户可以选择其中一个对象,并在应用层对其中一个对象执行操作,例如:
        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);
            }
        }
    

    关于domain-driven-design - DDD : Aggregate Roots,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2558469/

    相关文章:

    design-patterns - 丰富的领域模型和 ORM

    asp.net-mvc - 简单的注入(inject)器 : How to register event handlers instances to the event dispatcher

    django - 在汇总中使用模型方法

    r - 如何在每列中获得均值?

    domain-driven-design - 聚合根或应用程序服务中的用户权限检查?

    nhibernate - 使用 ActiveRecord 在 nHibernate 中急切加载延迟加载的实体

    doctrine - Doctrine 2 中的多态关联?

    design-patterns - 洋葱架构的 DDD 端口和适配器,哪里去了?

    c# - System.Threading.Tasks.TaskExceptionHolder.Finalize() 处的 System.AggregateException

    entity - 差异聚合根/实体