DDD:汇总根

扫码查看
本文介绍了DDD:汇总根的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要帮助找到我的总根和边界。

I need help with finding my aggregate root and boundary.

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

I have 3 Entities: Plan, PlannedRole and PlannedTraining. Each Plan can include many PlannedRoles and PlannedTrainings.

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

Solution 1: At first I thought Plan is the aggregate root because PlannedRole and PlannedTraining do not make sense out of the context of a Plan. They are always within a plan. Also, we have a business rule that says each Plan can have a maximum of 3 PlannedRoles and 5 PlannedTrainings. So I thought by nominating the Plan as the aggregate root, I can enforce this invariant.

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

However, we have a Search page where the user searches for Plans. The results shows a few properties of the Plan itself (and none of its PlannedRoles or PlannedTrainings). I thought if I have to load the entire aggregate, it would have a lot of overhead. There are nearly 3000 plans and each may have a few children. Loading all these objects together and then ignoring PlannedRoles and PlannedTrainings in the search page doesn't make sense to me.

解决方案2:我刚意识到用户希望再有2个搜索页面,他们可以在其中搜索计划角色或计划培训。这使我意识到他们正在尝试独立地访问这些对象,并且脱离计划的上下文。所以我认为我的最初设计不对,这就是我提出此解决方案的方式。因此,我认为这里有3个汇总,每个实体1个。

Solution 2: I just realized the user wants 2 more search pages where they can search for Planned Roles or Planned Trainings. That made me realize they are trying to access these objects independently and "out of" the context of Plan. So I thought I was wrong about my initial design and that is how I came up with this solution. So, I thought to have 3 aggregates here, 1 for each Entity.

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

This approach enables me to search for each Entity independently and also resolves the performance issue in solution 1. However, using this approach I cannot enforce the invariant I mentioned earlier.

还有另一个不变式,指出仅当计划处于某种状态时才可以更改。因此,我不应该将任何PlannedRoles或PlannedTrainings添加到不在该状态的计划中。同样,我无法使用第二种方法来强制执行此不变式。

There is also another invariant that states a Plan can be changed only if it is of a certain status. So, I shouldn't be able to add any PlannedRoles or PlannedTrainings to a Plan that is not in that status. Again, I can't enforce this invariant with the second approach.

任何建议将不胜感激。

Any advice would be greatly appreciated.

干杯,
Mosh

Cheers,Mosh

推荐答案

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

I was having similar problems with this when designing my model and asked this question which I think might help you, especially regarding your first point.

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

When it comes to searching I don't work with the 'model', instead I have specialised search repositories that return 'Summary' objects... i.e. 'PlanSummary'. These are nothing more than information objects (could be thought of more like reporting) and are not used in a transactional sense - I don't even define them in my model class library. By creating these dedicated repositories and types I can implement high performing search queries that can contain grouped data (such as a PlannedTraining count) without loading all of the associations of the aggregate in memory. Once a user selects one of these summary objects in the UI, I can then use the ID to fetch the actual model object and perform transactional operations and commit changes.

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

So for your situation I would provide these specialised search repositories for all three entities, and when a user wishes to perform and action against one, you always fetch the Plan aggregate that it belongs to.

通过这种方式,您可以进行绩效搜索,同时仍然保持单个合计与所需的不变量。

This way you have the performant searches whilst still maintaining your single aggregate with the required invariants.

编辑-示例:

好的,所以我认为实施是主观的,但这就是我在应用程序中以 TeamMember聚合为例进行处理的方式。用C#编写的示例。我有两个类库:

OK, so I guess implementation is subjective, but this is how I have handled it in my application, using a 'TeamMember' aggregate as an example. Example written in C#. I have two class libraries:


  • 模型

  • 报告

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

The Model library contains the aggregate class, with all invariants enforced, and the Reporting library contains this simple class:

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; }
}

报告库还包含以下接口:

The Reporting library also contains the following interface:

public interface ITeamMemberSummaryRepository : IReportRepository<TeamMemberSummary>
{

}

这是应用程序的界面层(在我的情况下恰好是WCF服务)将消耗并通过我的IoC容器(Unity)解析实现。 IReportRepository和基础ReportRepositoryBase都位于Infrastructure.Interface库中。因此,我的系统中有两种不同类型的存储库-聚合存储库和报告存储库...

This is the interface that the application layer (which in my case happens to be WCF services) will consume and will resolve the implementation via my IoC container (Unity). The IReportRepository lives in an Infrastructure.Interface library, as does a base ReportRepositoryBase. So I have two different types of repository in my system - Aggregate repositories, and reporting repositories...

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

Then in another library, Repositories.Sql, I have the implementation:

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()
    {

    }
}

然后,在我的应用程序层:

So then, in my application layer:

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

        return repository.FindAll(criteria);

    }

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

Then in the client, the user can select one of these objects, and perform an action against one in the application layer, for example:

    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);
        }
    }

这篇关于DDD:汇总根的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-04 16:30
查看更多