Архитектура PHP приложения на laravel (Repository, Интеграция с API)

Коллеги, хотел спросить как поступить, при проектировании приложения:

Есть два источника данных: БД и сторонний API

Пошастав по интернету, нашел, что и БД, и API нужно получать из репозиториев.

В целом с БД все ясно - создаем трейт с базовыми фунцкиями ORM (find, create, delete и т.д.), подключаем к репозиторию и используем в репе модель.

А вот как поступить с API?

Писать http запросы прям в репозитории, или выводить в отдельные классы? (т.к. там может присутствовать логика)

И получается, что я передаю массив данных в репозиторий, с репозитория передаю в другой класс. Я просто не уверен, нормально ли это


Ответы (1 шт):

Автор решения: Romka

Репозиторий инкапсулирует набор объектов, сохраняемых в хранилище данных, и операции выполняемые над ними.(источник)

На мой взгляд использовать репозитории для операций над данными полученнными по api - нормальный подход.


создаем трейт с базовыми фунцкиями ORM

Можно и через трейт. На ваше усмотрение.


Писать http запросы прям в репозитории, или выводить в отдельные классы

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

Репозиторий должен знать класс, через который он может получить доступ к данным.

На мой взгляд, простой пример реализации должен выглядеть так:

Api клиент:

class ApiProviderClient
{
    private $httpClient;
    private $address;
    private $key;

    public function __construct(\GuzzleHttp\Client $httpClient, string $address, string $key)
    {
        $this->httpClient = $httpClient;
        $this->address    = $address;
        $this->key        = $key;
    }

    public function getProducts(): array
    {
        $response = $this->httpClient->request('GET', $this->address, [
            'headers' => [
                'key' => $this->key,
            ],
        ]);

        return json_decode($response->getBody()->getContents())->products;
    }
}

Репозиторий:

class ProviderRepository
{
    private $apiClient;

    public function __construct(ApiProviderClient $apiClient)
    {
        $this->apiClient = $apiClient;
    }

    public function find(array $params): Collection
    {
        $products = $this->apiClient->getProducts();
        //other logic filters

        return new Collection($products);
    }
}

Таким образом, репозиторий занимается только своим делом - оперирует данными. Клиент для api знает все о взаимодействии с провайдером(адреса, ключи, форматы). Более того, клиент может понадобиться в других местах.

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


И получается, что я передаю массив данных в репозиторий, с репозитория передаю в другой класс. Я просто не уверен, нормально ли это

Это нормально. Главное позаботьтесь о том, чтобы репозиторий всегда возвращал одну и ту же структуру. Чтобы в случае замены клиента, репозиторий работал как и раньше. Для преобразования данных полученых от клиента к нужном типу(коллекции, объектам) можно воспользоваться Data Mapper.

→ Ссылка