У меня есть совокупный корень ProfileGroup, который состоит из авторизаций, которые позже можно назначать пользователям. ProfileGroup AR содержит следующие авторизации:
Код: Выделить всё
private List _authorizations = new();
public IReadOnlyCollection Authorizations => _authorizations;
Сами авторизации не создаются пользователями. Они предопределены и занесены в базу данных посредством миграции.
Каждая группа профилей может иметь до ~100 авторизаций. Группа профилей отвечает за поддержание этих авторизаций в допустимом состоянии (например, за обеспечение принадлежности авторизации правильному приложению).
Ранее логика добавления авторизации выглядела примерно так (псевдокод):
- Проверьте, существует ли группа профилей:
_context.ProfileGroups.AnyAsync(x => x.Id == id) - Проверьте, существует ли авторизация:
_context.ProfileGroupAuthorizations.FirstOrDefaultAsync(x => x.Id == id)
Если она не существует, создайте ее
Если она существует, обновите ее - Добавьте авторизацию через:
_context.ProfileGroupAuthorizations.Add(auth)
Только АГРЕГАТНЫЕ КОРНИ можно получить непосредственно с помощью запросов к базе данных. Все остальные объекты должны быть найдены путем обхода ассоциаций.
Основываясь на этом, я реализовал ProfileGroupRepository, который извлекает только совокупный корень. Я перенес операции CRUD на сам домен и инкапсулировал логику с помощью методов, которые изменяют список частных _authorizations, например
Код: Выделить всё
public void AddAuthorization()
public void RemoveAuthorization()
При получении корня агрегата всегда ли мне загружать весь агрегат, а затем выполнять операцию, или допустимо загружать только те дочерние объекты, которые мне нужны, например:
Код: Выделить всё
ProfileGroup? profileGroup = await _context.ProfileGroups .Include(x => x.Authorizations.Where(a => a.Id == authorizationId)) .FirstOrDefaultAsync(x => x.Id == profileGroupId);
Проблема заключается в том, что я использую шаблон исходящих данных для распространения этих изменений в N последующих баз данных, и загрузка всех дочерних объектов каждый раз кажется излишним.
Чтобы обеспечить соблюдение правил DDD, я понял, что мне не следует напрямую запрашивать или сохранять дочерние объекты за пределами корня агрегата. Однако для коллекций, которые могут содержать много строк, я не уверен, эффективна или необходима быстрая загрузка всего набора данных в агрегат.
Для агрегатов с небольшими коллекциями или отдельными дочерними объектами у меня есть разумный подход. Но когда дело доходит до более крупных отношений, подобных этой, или отношений «многие ко многим» в целом, я изо всех сил пытаюсь решить:
- Должен ли объект отношения сам быть совокупным корнем?
- Или он должен принадлежать одному из основных агрегатов?
- Как мне сбалансировать правила согласованности DDD с проблемами производительности и масштабируемости?
Подробнее здесь: https://stackoverflow.com/questions/798 ... egate-root
Мобильная версия