Статьи

Шаблон хранилища Демистифицированный

Что такое шаблон проектирования репозитория?

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

Почему ты должен заботиться?

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

Теперь, если мы все еще используем модели данных, у нас есть несколько тесно связанных приложений. Приложение витрины магазина должно знать все источники данных, с которыми оно должно взаимодействовать, и это плохой дизайн приложения. Работа приложения витрины здесь состоит в том, чтобы позволить клиентам размещать заказы на конфеты, приложение не должно заботиться об источнике данных, оно не должно отслеживать все различные источники данных. Здесь репозитории данных вступают в игру. В соответствии с шаблоном проектирования репозитория общедоступный API предоставляется через интерфейс, и каждый потребитель (в нашем случае это приложение нашего магазина) использует этот API для связи с источником данных. Какой источник данных используется или как он подключается, это не имеет значения для приложения. Приложение касается только тех данных, которые оно получает, и тех данных, которые оно отправляет для сохранения.

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

Это волшебная пуля?

Ну нет это не так. Как и у каждого шаблона дизайна, у него есть свои плюсы и минусы, плюсы и минусы.

Плюсы:

  • Разделение интересов; приложению не нужно знать или отслеживать какие-либо или все источники данных.
  • Позволяет легко модульное тестирование, так как репозитории привязаны к интерфейсам, которые внедряются в классы во время выполнения.
  • СУХОЙ (не повторяйте себя) дизайн, код для запроса и извлечения данных из источника (ов) данных не повторяется.

Минусы:

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

Как это сделать?

Давайте посмотрим на это с небольшим примером кода. Я буду использовать Laravel здесь в примере, чтобы использовать его превосходную функцию внедрения зависимостей. Если вы используете какой-либо современный PHP-фреймворк, у него уже должен быть контейнер Dependency Injection / IoC. Внедрение зависимостей требуется для реализации шаблона проектирования репозитория, поскольку без него вы не сможете привязать репозиторий данных к интерфейсу репозитория, и вся идея состоит в том, чтобы кодировать интерфейс, чтобы избежать жесткой связи. Если вы не используете какой-либо фреймворк или у вашего фреймворка нет контейнера IoC, вы можете использовать готовый контейнер IoC (см. Сноски).

Давайте взломать дальше. Во-первых, мы настроили наше пространство имен и автозагрузку в Composer. Откройте composer.json и добавьте автозагрузку psr-4 для нашего пространства имен (в узле autoload сразу после classmap ).

      "autoload" :   { 
         "classmap" :   [ 
             "app/commands" , 
             "app/controllers" , 
             "app/models" , 
             "app/database/migrations" , 
             "app/database/seeds" , 
             "app/tests/TestCase.php" 
         ], 
         "psr-4" :   { 
             "RocketCandy\\" :   "app/RocketCandy" 
         } 
     }, 

После сохранения выполните команду composer dump-autoload -o в терминале, чтобы зарегистрировать автозагрузку для нового пространства имен. Создайте OrangeCandyRepository.php в app/RocketCandy/Repositories/OrangeCandyRepository/ . Это будет интерфейс нашего репозитория.

 <? php namespace   RocketCandy \Repositories\OrangeCandyRepository ; 

 interface   OrangeCandyRepository   { 

     public   function  get_list (  $limit =   0 ,  $skip =   0   ); 

     public   function  get_detail (  $candy_id =   0   ); 

 } 

Теперь, когда у нас есть интерфейс, мы можем создать репозиторий. Создайте CityAOrangeCandyRepository.php в app/RocketCandy/Repositories/OrangeCandyRepository/ .

 <? php namespace   RocketCandy \Repositories\OrangeCandyRepository ; 

 class   CityAOrangeCandyRepository   implements   OrangeCandyRepository   { 

     public   function  get_list (  $limit =   0 ,  $skip =   0   )   { 
         //query the data source and get the list of 
         //candies 
     } 

     public   function  get_detail (  $candy_id =   0   )   { 
         //query the data source and get the details of 
         //candies 
     } 

 } 

Чтобы привязать репозиторий OrangeCandyRepository интерфейсу OrangeCandyRepository , мы будем использовать IoC-контейнер Laravel. Откройте app/start/global.php и добавьте следующее в конец файла.

 //OrangeCandyRepository 
 App :: bind ( 
     'RocketCandy\Repositories\OrangeCandyRepository\OrangeCandyRepository' , 
     'RocketCandy\Repositories\OrangeCandyRepository\CityAOrangeCandyRepository' 
 ); 

Примечание: я поместил привязку IoC в global.php только для демонстрации. В идеале они должны быть помещены в отдельный файл, в который вы поместите все привязки IoC и загрузите этот файл здесь, в global.php или создадите сервис-провайдеров для регистрации каждой привязки IoC. Вы можете прочитать больше здесь .

Теперь мы можем использовать репозиторий через интерфейс. В нашем CandyListingController.php расположенном в app/controllers/ .

 <? php use   RocketCandy \Repositories\OrangeCandyRepository\OrangeCandyRepository ; 

 class   CandyListingController   extends   BaseController   { 

     /** * @var RocketCandy\Repositories\OrangeCandyRepository\OrangeCandyRepository */ 
     protected  $_orange_candy ; 

     public   function  __construct (   OrangeCandyRepository  $orange_candy )   { $this -> _orange_candy =  $orange_candy ; 
     } 

 } 

Здесь мы OrangeCandyRepository интерфейс OrangeCandyRepository в наш контроллер и сохраняем ссылку на его объект в переменной класса, которая теперь может использоваться любой функцией в контроллере для запроса данных. Поскольку мы связали интерфейс CityAOrangeCandyRepository репозиторием CityAOrangeCandyRepository , это будет так, как если бы мы непосредственно использовали CityAOrangeCandyRepository репозиторий CityAOrangeCandyRepository .

Так что теперь тип и тип источника данных — единственная забота CityAOrangeCandyRepository здесь. Наше приложение знает только интерфейс OrangeCandyRepository и API, который оно предоставляет, которому должен соответствовать каждый реализующий его репозиторий. Репозиторий разрешается из контейнера IoC во время выполнения, что означает, что привязка интерфейса <=> к репозиторию может быть установлена ​​по мере необходимости, интерфейс может быть привязан к любому репозиторию данных, и наше приложение не должно беспокоиться об изменении данных источником, который теперь может быть базой данных, веб-службой или транс-мерным каналом гиперданных.

Один размер не подходит для всех

Как я уже упоминал выше в «Минусах шаблона проектирования репозитория», он немного усложняет приложение. Поэтому, если вы создаете небольшое приложение и не видите его перехода в высшую лигу, где может быть задействовано более одного источника данных, вам лучше не реализовывать это и придерживаться старых добрых моделей данных. Знание чего-либо отличается от знания того, когда его использовать. Это очень удобный шаблон проектирования, который экономит много головной боли как при создании приложения, так и когда его необходимо поддерживать или масштабировать (или уменьшать), но это не волшебная палочка для каждого приложения.

Я использовал специальный код Laravel для демонстрации реализации выше, но он довольно прост и похож на любой приличный контейнер IoC. Есть вопросы? Огонь в комментариях ниже.

Примечания: