Подтвердить что ты не робот

Шаблон хранилища: как ленивая загрузка? или, должен ли я разделить этот агрегат?

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

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

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

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

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

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

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

Я неправильно интерпретирую намерение шаблона репозитория?

4b9b3361

Ответ 1

Я неправильно интерпретирую намерение шаблона репозитория?

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

Немного упростите его и придерживайтесь конструкторов вместо того, чтобы сначала создавать методы:

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 или Editor вы правы, или им нужно будет получить доступ к репозиториям и что это нехорошо. Вышеприведенный фрагмент слабо связан, но он не может использоваться повторно. Вы только что достигли границы шаблона репозитория.

Далее - это слой адаптера для вашего приложения с общим источником репозиториев (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 будет завершен, вы должны оставить ассоциации в одиночку и переместить иерархию объектов/слоев на интересные части, такие как адаптеры и кэши и бизнес-логика ( "О, мой!" ).

Ответ 2

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

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

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

Помогает ли это чему-то или просто усложняет работу?

Ответ 3

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

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

Если вы разделите Агрегат, вы можете исследовать шаблон Unit of Work как одно из решений для атомарности. Однако эта проблема не уникальна для DDD, и я уверен, что существуют другие решения для транзакционного поведения.

Ответ 4

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

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

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

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

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