Практическое использование шаблонов Unit Of Work и репозитория

#orm #domain-driven-design #repository-pattern #unit-of-work #domain-model

#orm #дизайн, управляемый доменом #репозиторий-шаблон #единица работы #модель домена

Вопрос:

Я создаю 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();
  

Следует ли, как в этом примере, использовать единицу работы и репозиторий независимо, или:

  • Должна ли Unit Of Work внутренне использовать репозиторий и иметь возможность загружать объекты?
  • … или репозиторий должен использовать внутренне Unit Of Work и автоматически присоединять любой загруженный объект?

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

Комментарии:

1. Просто любопытно, зачем создавать ORM, когда есть несколько хороших?

2. Просто пытаюсь применить лучшие практики ORM к PHP. Несколько из них уже есть, но не совсем соответствуют моим ожиданиям.

3. Спасибо, что я нашел такой вопрос 1

Ответ №1:

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

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

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

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

Комментарии:

1. Спасибо за этот полный ответ. В заключение, «лучшего» способа сделать это не существует, это вопрос выбора в глобальном контексте ORM.

Ответ №2:

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

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

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

Ответ №3:

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

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

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

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

Комментарии:

1. Спасибо Nilesh. На самом деле мой план состоит в том, чтобы определить чистый интерфейс для всех этих шаблонов, наряду с базовой реализацией, которая стремится быть очень универсальной и адаптировать большинство ситуаций для нашей повседневной работы. У меня уже есть рабочая реализация этого, с моделями и картографами данных, а также с реестром картографов, который предоставляет доступ ко всем картографам в одном классе; но я пытаюсь раздвинуть границы, чтобы обеспечить поддержку репозитория, содержащего карту идентификации, с поддержкой поиска по критериям и для единицы работы, для управления обновлениями в базе данных и для обработки транзакций.

2. Извините за многословие, мне нужно больше места 🙂 Итак, я хотел бы найти общую идею о том, как собрать все это вместе! Мы высоко ценим вашу помощь.