Ожидаемое поведение репозитория

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

interface IPersonRepository
{
    public function find(Criteria criteria);
    public function add(Person person);
    public function delete(Person person);
}

По словам Фаулера (PoEAA, стр. 322):

Репозиторий служит посредником между слоями отображения домена и данных, действуя как коллекция объектов домена в памяти. [...] Объекты можно добавлять и удалять из репозитория, как и из простого набора объектов.

Это будет означать, что следующий тест должен работать (при условии, что у нас уже есть персона, чья фамилия Фаулер):

collection = repository.find(lastnameEqualsFowlerCriteria);
person = collection[0];

assertEquals(person.lastname, "Fowler");

person.lastname = "Evans";
newCollection = repository.find(lastnameEqualsFowlerCriteria);

assertFalse(newCollection.contains(person));

Это означает, что при сопоставлении с базой данных, даже если где-то не был вызван явный метод save(), модель Person должна быть автоматически сохранена репозиторием, чтобы следующий запрос возвращал правильную коллекцию, не, содержащий исходный объект Person.

Но разве это не роль единицы работы — решать, какую модель сохранять в базе данных и когда?

В приведенной выше реализации репозиторий должен принять решение о сохранении ранее полученного объекта Person при получении другого вызова find(), чтобы результат соответствовал модификации. Но если бы не было другого вызова find(), модель вообще не сохранялась бы неявно.

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


person BenMorel    schedule 18.05.2011    source источник


Ответы (2)


Репозиторий служит посредником между слоями отображения домена и данных, действуя как коллекция объектов домена в памяти. [...] Объекты можно добавлять и удалять из репозитория, как и из простого набора объектов.

Это не означает, что вам не нужен метод сохранения. Вам по-прежнему необходимо явно зафиксировать изменения в хранилище.

См. Шаблон единицы работы и игнорирование постоянства

public interface IUnitOfWork {
  void MarkDirty(object entity);
  void MarkNew(object entity);
  void MarkDeleted(object entity);
  void Commit();
  void Rollback();
}

В некотором смысле вы можете думать о единице работы как о месте, где можно сбросить весь код обработки транзакций. Обязанности Рабочей группы заключаются в следующем:

  • Управляйте транзакциями.
  • Порядок вставки, удаления и обновления базы данных.
  • Предотвращение дублирования обновлений. Внутри одного использования объекта «Единица работы» разные части кода могут помечать один и тот же объект «Счет-фактура» как измененный, но класс «Единица работы» выдает только одну команду UPDATE для баз данных.
person Todd Smith    schedule 18.05.2011
comment
Спасибо, Тодд, но пока мой вопрос был сосредоточен на шаблоне репозитория. Вы имеете в виду, что я должен добавить функцию save(Person person) в репозиторий? Но что, если для тестов я использую стратегию памяти для своего репозитория, а для создания стратегии базы данных; Я получу разные результаты от обоих, потому что состояние памяти вернет другую коллекцию для второго вызова find (), даже если Person не был сохранен ()? - person BenMorel; 19.05.2011
comment
Я бы написал тесты, которые сосредоточены на простых операциях CRUD, выполняющих одно чтение/обновление/проверку. Обычно вы не хотите запускать серию тестов, используя один и тот же экземпляр вашего репозитория в памяти. Я всегда создаю новый репозиторий памяти для каждого запускаемого метода тестирования. - person Todd Smith; 20.05.2011
comment
Понял, в этом больше смысла. Я все еще думаю, что репозиторий, который на 100% соответствует описанию PoEAA, - это что-то действительно сложное, если не невозможное в реальном мире! Спасибо за ваши комментарии. - person BenMorel; 21.05.2011
comment
Обновление: я только что узнал, что ORM, такие как Hibernate, решают эту проблему, автоматически сбрасывая изменения в базу данных при вызове find(), чтобы гарантировать согласованное чтение. Однако это по-прежнему должно быть выполнено в контексте единицы работы, иначе при необходимости изменения нельзя будет откатить. - person BenMorel; 06.06.2011