Как не нужно использовать паттерн Repository

Как не нужно использовать паттерн Repository

Данная статья является неким опытом, который был приобретен в результате весьма неприятной архитектурной ошибки, допущенной мной при длительной разработке проекта на Laravel5.

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

Введение

Сразу хочу предупредить, что статья скорее ориентирована на разработчиков, которые только знакомятся с паттернами проектирования, читают умные книжки, а потом пытаются все это дело применить, так сказать, в «продакшине». Весьма в тему будет упомянуть разработку с помощью frameworks, которые используют ActiveRecord (Например Yii, Laravel и др.), ведь именно благодаря ActiveRecord я продолжаю наступать на грабли и учиться решать различные проблемы.

Паттерн Repository

Подробнее об этом паттерне можно прочитать:

  • Паттерн «Репозиторий». Основы и разъяснения
  • Хранилище (Repository)
  • А так-же во многих книгах о программировании (Мартин Фаулер и др.)
Старт с Repository

Если Вы разрабатывали средние и/или большие (не в плане нагрузки, а скорее с большой кодовой базой и длительной поддержкой) проекты, то скорее всего сталкивались с недостатками и проблемами, которые возникают при использовании ActiveRecord. Основные можно выделить в небольшой список:

  • Нарушение единой ответственности.
  • Из первого пункта следует, что Ваши «модели» могут быть весьма «жирными».
  • У новичков формируется неправильное понятие MVC, где M понимают как модель и это == 1 класс, преимущественно ActiveRecord.
  • Весьма ресурсозатратная организация импорта/экспорта данных, если нужно по какой-то причине работать с большим кол-во записей за раз.
  • Неудобно, а иногда и не реально писать кастомные запросы на SQL в случае необходимости.

Так вот, за несколько лет работы с frameworks в основе которых лежит ActiveRecord, я сталкивался скорее-всего со всеми его недостатками. И как-то начитавшись умных книжек и статей, при проектировании архитектуры нового проекта, я решил внедрить паттерн Repository.

Исходя из его простой реализации и описания — все просто: берем интерфейсы, биндим их на классы, которые будут нашими репозиториями достающими данные из «хранилища». Все отлично, в любом момент можем забиндить другой Repository, подменить реализацию методов выборки, в общем все четко.

Пошел поменял статус на Systems Architect

А действительно ли Ваш «репозиторий» это Repository ?

И вот произошел момент, когда мне действительно нужно было подменить реализацию. Я приехал в офис с улыбкой и с мыслями: «Как я все легко подменю, просто создам другой класс и поменяю строку при биндинге».

Однако задача стояла, подменить выборку в первом случае из файла, а в другом из стороннего API. Когда я начал копаться и разбираться во всем этом деле, я заметил, что мои «репозитории» возвращают модели. Да, все верно, мой якобы паттерн Repository возвращает все те-же модели, которые продолжают гулять по всему проекту.

Да, благодаря интерфейсу я действительно смог легко подменить реализацию, однако формат возвращаемых данных изменился. Ранее это был экземляр класс с ActiveRecord, однако теперь мой репозиторий мог возвращать массив или коллекцию.

Что это значит? Это значит, что любой представитель моей команды, мог использовать индивидуальные особенности «модели». Например мутаторы или аксессоры, или написать метод в модель с логикой и вызывать его где-угодно. Поэтому подменив реализацию, я изменил формат данных и теперь не могу гарантировать, что все приложение будет работать как работало, так-как могло произойти что угодно. Начиная от обращения к безобидному методу модели в любом месте, и заканчивая вызовом save() в виде. Никто не знает, никто не помнит, особенно если проект пережил несколько разработчиков, которые ушли и на смену им пришли новые.

Без паники, тесты

Потом я вспомнил, что можно запустить тесты. Я повернулся к напарнику и спросил: «ты писал тесты ?». Он в свою очередь повернулся к другому коллеге и уточнил этот-же вопрос. В общем как оказалось не очень большой % нашего приложения был покрыт тестами.

Что мы имеем?

Итак, у нас вышел дополнительный слой абстракции, который требует большего порога входа и больше времени на разработку с КПД стремящимся в 0, так-как модели как гуляли по проекту, так и продолжают гулять.

Пошел поменял статус на Junior Assistant

Более детально разбираемся в проблеме

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

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

Перерывая информацию по теме, я нашел вот такое приложение на Laravel:

Давайте посмотрим на пример UserRepository:

Один из методов я хочу разобрать тут (на случай, если все это пропадет):

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

Как использовать Repository правильно?

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

Реализация паттерна Repository или что-то типа того

Я нашел статью, в которой описывается реализация паттерна Repository для Laravel5 (скорее всего для Yii2 будет примерно то-же самое). Однако по моему личному мнению в ней скорее описывается структурированный подход к написанию запросов с помощью ActiveRecord. С одной стороны удобно, уменьшаются дубли кода, худеют модели и архитектура более изящна. С другой стороны Repository не совсем выполняют свою роль «абстрактного хранилища», так-как идет работа с моделями и полная привязка к ActiveRecord со всей его магией.

Опасность может быть в следующем: при смене источника данных (обратите внимание, не обязательно менять базу или framework, достаточно получить данные из друго-го ресурса, например из стороннего API или сделав сложный кастомный запрос с помощью query builder), если Вы работали с моделями, а новая реализация вернет массив или коллекцию, то скорее всего Вы не сможете гарантировать стабильную работу Вашего приложения. Так-как попросту Вы не знаете (если проект большой и пишется не только Вами), какие методы, аксессоры/мутаторы и прочие прелести моделей были использованы и где.

Выводы

Получив полезный и в то-же время горький опыт при проектировании приложения, для себя я могу подчеркнуть следующие выводы, которыми хочу поделиться (возможно это будет кому-то полезно):

📎📎📎📎📎📎📎📎📎📎