DDD: Aggregate Roots

我的梦境 提交于 2019-11-30 04:04:57
David Masters

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.

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

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.

Edit - Example:

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:

  • Model
  • Reporting

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

}

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...

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

Real problem here is SRP violation. Your input part of app goes in conflict with output.

Stick with first solution (Plan==aggregate root). Artificially promoting entities (or even value objects) to aggregate roots distorts whole domain model and ruins everything.


You might want to check out so called CQRS (command query responsibility segregation) architecture which would fit perfectly to fix this particular issue. Here's an example app by Mark Nijhof. Here's nice 'getting-started' list.

This is the whole point of CQRS architectures: segregate Commands - that modify the domain - from Queries - that simply give a view of domain state, because requirement for Commands and Queries are so different.

you can find a good introductions on these blogs :

and on many other blogs (including mine)

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!