Шаблон репозитория: как выполнить Lazy Load? или я должен разделить этот агрегат? - PullRequest
39 голосов
/ 19 января 2009

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

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

Я использую подход 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 ]

30 голосов
/ 14 мая 2009

Я неправильно истолковал намерение шаблона 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 завершен, вам нужно оставить ассоциации в покое и перейти вверх по иерархии объектов / слоев к забавным частям, таким как Adapters and Caches и Business Logic. ( «О, боже мой!» ).

4 голосов
/ 23 января 2009

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

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

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

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

3 голосов
/ 23 января 2009

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

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

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

0 голосов
/ 11 февраля 2009

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

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

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

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

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

...