Практическое использование шаблонов Unit of Work & Repository - PullRequest
17 голосов
/ 19 мая 2011

Я строю ORM и пытаюсь выяснить, каковы точные обязанности каждого шаблона. Допустим, я хочу перевести деньги между двумя учетными записями, используя Unit Of Work для управления обновлениями в одной транзакции базы данных. Правильный ли следующий подход?

  1. Получить их из хранилища
  2. Прикрепите их к моей единице работы
  3. Есть ли бизнес-транзакция и коммит?

Пример:

from = acccountRepository.find(fromAccountId);
to = accountRepository.find(toAccountId);

unitOfWork.attach(from);
unitOfWork.attach(to);    

unitOfWork.begin();
from.withdraw(amount);
to.deposit(amount);
unitOfWork.commit();

Должны ли, как в этом примере, единица работы и хранилище использоваться независимо, или:

  • Должна ли единица работы внутренне использовать хранилище и иметь возможность загружать объекты?
  • ... или хранилище должно использовать внутреннюю единицу работы и автоматически присоединять любую загруженную сущность?

Все комментарии приветствуются!

Ответы [ 3 ]

16 голосов
/ 28 мая 2011

Коротким ответом будет то, что хранилище каким-то образом будет использовать UoW, но я думаю, что взаимосвязь между этими шаблонами менее конкретна, чем это может показаться на первый взгляд. Цель этой единицы работы - создать способ объединить группу функций, связанных с базой данных, чтобы они могли выполняться как элементарный элемент. Часто существует связь между границами, созданными при использовании UoW, и границами, создаваемыми транзакциями, но эта связь является скорее совпадением.

Шаблон Repository, с другой стороны, представляет собой способ создания абстракции, напоминающей коллекцию, через Aggregate Root. Чаще всего вещи, которые вы видите в репозитории, связаны с запросами или поиском экземпляров Aggregate Root. Более интересный вопрос (и тот, который не имеет единственного ответа) заключается в том, имеет ли смысл добавлять методы, которые имеют дело с чем-то другим, кроме запросов к агрегатам. С одной стороны, могут быть некоторые допустимые случаи, когда у вас есть операции, которые будут применяться к нескольким агрегатам. С другой стороны, можно утверждать, что если вы выполняете операции над несколькими агрегатами, вы фактически выполняете одно действие над другим агрегатом. Если вы только запрашиваете данные, я не знаю, действительно ли вам нужно создавать границы, подразумеваемые UoW. Все сводится к домену и как это моделируется.

Два паттерна имеют дело с очень разными уровнями абстракции, и участие Единицы Работ будет зависеть от того, как моделируются Агрегаты. Агрегаты могут хотеть делегировать работу, связанную с сохранением, сущностям, которыми они управляют, или может существовать другой уровень абстракции между Агрегатами и фактическим ORM. Если ваши Агрегаты / Субъекты сами имеют дело с постоянством, то может оказаться целесообразным, чтобы хранилища также управляли этим постоянством. Если нет, то нет смысла включать UoW в ваш репозиторий.

Если вы хотите создать что-то для общего публичного потребления вне вашей организации, то я бы предложил создать ваши интерфейсы репозитория / базовые реализации таким образом, чтобы они могли напрямую взаимодействовать с вашим ORM или нет, в зависимости от потребностей. пользователя вашего ORM. Если это внутреннее, и вы выполняете постоянную работу в ваших Агрегатах. Сущностях, то имеет смысл, чтобы ваш Репозиторий использовал ваше UoW. Для универсального репозитория имеет смысл предоставить доступ к объекту UoW из реализаций репозитория, который может обеспечить его инициализацию и надлежащее удаление. На этой ноте также будут случаи, когда вы, вероятно, захотите использовать несколько репозиториев в пределах одной границы UoW, поэтому вы захотите иметь возможность передавать уже заполненный UoW в репозиторий в этом случае.

4 голосов
/ 23 мая 2011

Я рекомендую вам использовать подход, когда хранилище использует UoW внутри. Этот подход имеет некоторые преимущества, особенно для веб-приложений.

В веб-приложении рекомендуемая схема использования UoW - единица работы (сеанс) на HTTP-запрос. Таким образом, если ваши репозитории будут использовать UoW, вы сможете использовать кэш 1-го уровня (используя карту идентификаторов) для объекта, запрошенного другими репозиториями (например, словарями данных, на которые ссылаются несколько агрегатов). Также вам придется совершать только одну транзакцию вместо нескольких, чтобы она работала намного лучше с точки зрения производительности.

Вы можете взглянуть на исходные коды Hibernate / NHibernate, которые являются зрелыми ORM в мире Java / .NET.

2 голосов
/ 19 мая 2011

Хороший вопрос!

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

Если ваша единица работы будет в значительной степени соответствовать репозиторию, то я бы выбрал второй вариант.

Однако мой вопрос к вам: как вы можете беспокоиться о хранилище при написании ORM? Они будут определены и использованы потребителями вашей единицы работы, верно? Если это так, у вас нет другого выбора, кроме как просто предоставить Единицу работы, и ваши потребители должны будут указать хранилища вместе с вашей единицей работы, а также будут нести ответственность за контроль границ единицы работы. Не так ли?

...