首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >DDD:聚合根

DDD:聚合根
EN

Stack Overflow用户
提问于 2010-04-01 14:13:02
回答 3查看 3.3K关注 0票数 6

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

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

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

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

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

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

还有另一个不变量,它规定只有当计划处于特定状态时才能更改它。因此,我不能向未处于该状态的计划添加任何PlannedRoles或PlannedTrainings。同样,我不能在第二种方法中强制使用这个不变量。

任何建议都将不胜感激。

干杯,莫什

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2010-04-03 04:23:53

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

DDD - How to implement high-performing repositories for searching

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

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

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

编辑-示例:

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

  • Model
  • Reporting

Model库包含aggregate类,并强制执行所有不变量,Reporting库包含以下简单类:

代码语言:javascript
复制
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; }
}

报告库还包含以下接口:

代码语言:javascript
复制
public interface ITeamMemberSummaryRepository : IReportRepository<TeamMemberSummary>
{

}

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

然后在另一个库Repositories.Sql中,我有这样的实现:

代码语言:javascript
复制
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()
    {

    }
}

那么,在我的应用层中:

代码语言:javascript
复制
    public IList<TeamMemberSummary> FindTeamMembers(TeamMemberCriteria criteria)
    {
        ITeamMemberSummaryRepository repository 
            = RepositoryFactory.GetRepository<ITeamMemberSummaryRepository>();

        return repository.FindAll(criteria);

    }

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

代码语言:javascript
复制
    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);
        }
    }
票数 9
EN

Stack Overflow用户

发布于 2010-04-10 20:30:29

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

坚持使用第一个解决方案(Plan==aggregate根)。人为地推动实体(甚至价值对象)聚合根,会扭曲整个域模型,并破坏一切。

您可能希望检查所谓的CQRS (命令、查询、责任、隔离)体系结构,该体系结构非常适合解决此特定问题。Here's an example app作者: Mark Nijhof。这是一个很好的'getting-started'列表。

票数 4
EN

Stack Overflow用户

发布于 2010-04-14 23:26:23

这就是CQRS体系结构的全部要点:将修改域的命令从查询中分离出来,这些命令只是给出域状态的视图,因为对命令和查询的要求是如此不同。

你可以在这些博客上找到一个很好的介绍:

  • Udi Dahan
  • Greg Young

还有很多其他的博客(包括mine)

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/2558469

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档