У меня были похожие проблемы с этим при проектировании моей модели, и я задал этот вопрос, который, я думаю, мог бы помочь вам, особенно в отношении вашего первого замечания.
DDD - Как реализовать высокопроизводительные репозитории для поиска .
Когда дело доходит до поиска, я не работаю с «моделью», вместо этого у меня есть специализированные поисковые репозитории, которые возвращают объекты «Сводка» ... т.е. «PlanSummary». Это не что иное, как информационные объекты (их можно больше рассматривать как отчеты), и они не используются в транзакционном смысле - я даже не определяю их в своей библиотеке классов моделей. Создав эти специализированные репозитории и типы, я могу реализовать высокоэффективные поисковые запросы, которые могут содержать сгруппированные данные (например, счет PlannedTraining) без загрузки всех ассоциаций агрегата в память. После того, как пользователь выберет один из этих итоговых объектов в пользовательском интерфейсе, я смогу использовать идентификатор для извлечения фактического объекта модели, выполнения транзакций и фиксации изменений.
Так что для вашей ситуации я бы предоставил эти специализированные поисковые репозитории для всех трех сущностей, и когда пользователь желает выполнить одно действие и действовать против него, вы всегда выбираете агрегат Плана, которому он принадлежит.
Таким образом, вы выполняете поиск по исполнителю, при этом сохраняя единый агрегат с необходимыми инвариантами.
Редактировать - Пример:
ОК, так что я думаю, что реализация субъективна, но именно так я и обработал ее в своем приложении, используя в качестве примера агрегат TeamMember. Пример написан на C #. У меня есть две библиотеки классов:
Библиотека Model содержит агрегатный класс со всеми принудительными инвариантами, а библиотека Reporting содержит этот простой класс:
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; }
}
Библиотека отчетов также содержит следующий интерфейс:
public interface ITeamMemberSummaryRepository : IReportRepository<TeamMemberSummary>
{
}
Это интерфейс, который прикладной уровень (который в моем случае оказывается службами 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()
{
}
}
Итак, на моем прикладном уровне:
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);
}
}