Шаблон репозитория: как выполнить Lazy Load? или я должен разделить этот агрегат?

У меня есть модель предметной области, которая имеет концепцию редактора и проекта.

Редактору принадлежит несколько проектов, и у проекта есть не только владелец редактора, но и несколько членов редактора. Таким образом, редактор также имеет ряд "объединенных" проектов.

Я использую подход DDD к моделированию этого и использую шаблон репозитория для постоянства. Тем не менее, я пока недостаточно хорошо разбираюсь в паттерне, чтобы определить, как мне это сделать.

Я работаю в предположении, что Editor и Project потенциально находятся в одной совокупности с корневым каталогом Editor. Поэтому я могу получить Редактора, а затем перечислить его Проекты и оттуда перечислить Редакторов-членов Проектов.

Однако, если мне разрешено получать редакторы только из моего репозитория, не означает ли это, что мне нужно загружать все проекты из репозитория, когда я получаю редактор, которому они принадлежат? А если я захочу лениво загрузить редакторов-участников, проекту понадобится также ссылка на репозиторий?

В качестве альтернативы, если я разделю агрегат и получу репозиторий Editor и репозиторий Project, как мне следует обрабатывать транзакции между ними, например, когда новый проект добавляется в Editor? Например:

Editor e = new Editor("Editor Name");
editorRepository.Add(e);

Project p = e.CreateProject("Project Name");
projectRepository.Add(p);    // These two lines
editorRepository.Save(e);    // should be atomic

Я неправильно истолковываю намерение шаблона Repository?

4 ответа

Решение

Я неправильно истолковываю намерение шаблона Repository?

Я собираюсь сказать "да", но знайте, что я и все, с кем я работал, спрашивали об одном и том же по одной и той же причине... "Вы не думаете о 4-м измерении, Марти".

Давайте немного упростим это и остановимся на конструкторах вместо методов Create:

Editor e = new Editor("Editor Name");
e = editorRepository.Add(e);

Project p = new Project("Project Name", e);
p = projectRepository.Add(p);

Внизу, ваш репозиторий проекта всегда хранит действительного владельца (p.EditorId) в данные проекта по мере его создания, и как бы вы не заполняли проекты редактора, он будет там. Вот почему рекомендуется помещать все необходимые свойства в конструкторы. Если вы не хотите передавать весь объект, просто e.Id Сделаю.

А если я захочу лениво загрузить редакторов-участников, проекту понадобится также ссылка на репозиторий?

Теперь, что касается того, как повторно заполнить проекты редактора по требованию, у вас есть несколько вариантов в зависимости от того, что вы собираетесь. Прямой репозиторий говорит, что вы хотите:

IEnumerable<Project> list = projectRepository.GetAllProjects()
                                .Where(x => x.editorId == e.Id);

Но где это поставить? Не внутри Project или редактора, вы правы, или им придется получить доступ к репозиториям, и это не хорошо. Вышеуказанный фрагмент слабо связан, но сам по себе не может быть повторно использован. Вы только что достигли пределов шаблона репозитория.

Далее следует уровень адаптера для вашего приложения с общим источником репозиториев (StaticServiceWrapper) или какой-то объект EditorAdapter (или Aggregate, или как вы их называете), или теперь вы можете смешивать методы расширения, которые могут свободно общаться с любыми необходимыми репозиториями. Я не сделал это точно в производственной системе, но чтобы показать вам краткий пример:

public static class Aggregators
{
    // one to one, easy
    public static Editor GetOwner(this Project p)
    {
        return StaticServiceWrapper.editorRep.GetEditorById(p.editorId);
    }

    // one to many, medium
    public static IEnumerable<Project> GetProjects(this Editor e) 
    { 
        return StaticServiceWrapper.projectRep.GetAllProjects()
                .Where(x => x.editorId == e.Id);
    }

    // many to many, harder
    public static IEnumerable<Editor> GetMembers(this Project p)
    {
        var list = StaticServiceWrapper.projectMemberMap.GetAllMemberMaps()
                        .Where(x => x.projectId == p.projectId);

        foreach ( var item in list )
            yield return StaticServiceWrapper.editorRep.GetEditorById(item.editorId);
    }
}

По сути, как только ваш GetAll,GetById,Add,Update,Remove Object Repository завершен, вы должны оставить ассоциации в покое и перейти вверх по иерархии объектов / слоев к забавным частям, таким как адаптеры и кеши и бизнес-логика ("О Мое! ").

Как насчет разделения обязанностей на EditorOwner и EditorMember?

Не зная вашего домена, я бы предположил, что у них будут разные обязанности - например, EditorOwner может быть довольно богатым (и может быть совокупным корнем), но Проекту может потребоваться лишь ограниченное количество информации о его участниках, поэтому объект EditorMember может быть довольно легким.

Эти доменные объекты также могут относиться к Пользователям, но это будет в другом контексте.

Это помогает или просто усложняет?

Это зависит от потребностей вашего приложения. Если загрузка всех проектов для данного редактора является большой проблемой, попробуйте ленивый шаблон загрузки, например, виртуальный прокси.

Что касается ленивой загрузки редакторов-членов проекта, если вы используете виртуальный прокси-сервер, я не вижу проблемы с добавлением прокси-сервера в EditorRepository, поскольку не считаю прокси частью домена.

Если вы разделите Агрегат, вы можете исследовать модель Единицы Работы как одно из решений атомарности. Эта проблема, тем не менее, не является уникальной для DDD, и я уверен, что есть другие решения для транзакционного поведения.

Здесь у вас есть 2 разных отношения, одно для владения и одно для членства.

Отношение собственности простое ко многим (один владелец для каждого проекта). Отношение членства много ко многим (много редакторов по проектам, много проектов по редакторам).

Вы можете предоставить свойство Owner в классе Project и предоставить метод в ProjectRepository, чтобы получить все проекты, принадлежащие определенному редактору.

Для связи "многие" предоставьте свойство Members в классе Project и метод в ProjectRepository, чтобы получить все проекты, содержащие указанный редактор в качестве члена.

Также кажется, что редакторы и проекты - это сущности, я бы, вероятно, разделил совокупность, но, возможно, эти термины имеют конкретное значение в вашем контексте, что делает их сущностями совокупности.

Другие вопросы по тегам