Архитектура PHP приложения на laravel (Repository, Интеграция с API)
Коллеги, хотел спросить как поступить, при проектировании приложения:
Есть два источника данных: БД и сторонний API
Пошастав по интернету, нашел, что и БД, и API нужно получать из репозиториев.
В целом с БД все ясно - создаем трейт с базовыми фунцкиями ORM (find, create, delete и т.д.), подключаем к репозиторию и используем в репе модель.
А вот как поступить с API?
Писать http запросы прям в репозитории, или выводить в отдельные классы? (т.к. там может присутствовать логика)
И получается, что я передаю массив данных в репозиторий, с репозитория передаю в другой класс. Я просто не уверен, нормально ли это
Ответы (1 шт):
Репозиторий инкапсулирует набор объектов, сохраняемых в хранилище данных, и операции выполняемые над ними.(источник)
На мой взгляд использовать репозитории для операций над данными полученнными по 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.