Как использовать внешний RESTful API с Symfony?

10

Мы создаем микросервисную архитектуру для наших проектов, в которой в основном интерфейсные приложения Symfony взаимодействуют с внутренними API RESTful.

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

Например, для объекта Client:

  • Используя Doctrine, мы просто должны определить наш класс Client, и теперь стало легко создавать, обновлять, извлекать наших клиентов.
  • Используя подход REST API, клиенты доступны через API, но у нас много работы, чтобы определить, как клиент создается (POST), обновляется (PUT), извлекается (GET) и т. Д.

Следует отметить, что клиенты используются несколькими приложениями, а не только интерфейсным приложением, следовательно, выделенным API.

Должны ли мы создавать классы с сущностно-подобными методами, скрывающими сложность вызовов API, импортирующими все данные API локально и получающими к ним доступ через Doctrine или любым другим способом?

Пьер Б.
источник
Я в той же лодке, что и ты. Использование внешних API с клиентами, сгенерированными из спецификаций OpenApi / Swagger. Интересно узнать о передовых методах потребления жизненного цикла, эксплуатации, параметрах и генерации фильтров. В настоящее время мой поиск распространяется на любые подходы, независимо от того, специфичен ли он для Symfony или нет.
вверх по течению
Проработав эту проблему в течение нескольких месяцев и вернувшись к этому вопросу, оба ответа до сих пор дают похожее решение: абстрагирование вызовов API с помощью popo. Это то, как мы в конечном итоге использовали, хотя существуют и другие решения. В аналогичных контекстах связи Webapp <> API использование уровня абстракции, скрывающего вызовы API от Webapp, кажется хорошим решением. С появлением микросервисов и подходов, основанных на API, несомненно, появятся соответствующие передовые практики и инструменты для решения общей проблемы.
Пьер Б.
Здесь был применен аналогичный подход. Бизнес-логика теперь содержится в слое 'action', который не заботится о том, является ли это REST api или командой cli, вызывающей его. Шестиугольный дизайн Алистера Кокберна стал отличной отправной точкой в ​​нашем случае: alistair.cockburn.us/Hexagonal+architecture
upstream

Ответы:

2

Я сделал проект на основе Symfony, который использует внешний API (JSON); я создал независимую клиентскую библиотеку («клиентская библиотека» - часть программного обеспечения, пакет композитора) с собственным набором сущностей (POPO); он интегрируется с платформой с использованием интерфейсов, предоставляемых Symfony (например, путем простого создания пользовательского поставщика ).

Клиент делает http-вызовы «закулисными», что важно для будущих возможностей тестирования. Вы не хотите раскрывать способ общения с источником данных, а также не хотите, чтобы ваши тесты опирались на живые API.

Интерфейс клиентской библиотеки (пример того, как это может выглядеть):

class ApiClient {

   /**
    * @throws SomeApiException If credentials are invalid
    * @return ApiUser
    */
   public function authenticate($username, $password);

   /**
    * @return ApiUser
    */
   public function findUserByEmail($email);

   /**
    * @throws SomeApiException If email is invalid
    * @return void
    */
   public function changeUserEmail(User $user, $newEmail);
}

Клиентская библиотека внутренне использует Guzzle для связи и компонент Doctrine Cache для кэширования результатов. Отображение между объектами-сущностями и json было сделано мапперами, которые когда-то писали - менялись не очень часто (или вообще не происходили). В этом случае я бы предложил использовать JMS Serializer для автоматического преобразования в JSON и обратно (я предполагаю, что вы используете JSON).

Вам понадобится хороший механизм кэширования и локальное хранилище, например, Redis. Выполнение api-вызовов для каждого запроса приложения убьет ваш сервер и резко замедлит работу вашего приложения. Очень важно понимать, как работают http-кэши. Если ваш API-интерфейс не использует кэширующие заголовки (или использует его неясным образом), будет очень сложно и ресурсоемко отслеживать изменения.

Вам также захочется подумать о том, как должен вести себя клиент в случае разрыва соединения - должен ли клиент использовать заблокированные данные? Было бы неплохо использовать прокси-сервер между вашим приложением и API. В этом случае прокси (например, Varnish) может ускорить ваши запросы, а также обновить заблокированные данные в фоновом режиме, не замедляя работу приложения. Он также будет поддерживать ваш сайт в сети в случае сбоя API. В то же время вы не сможете записывать данные, но ваши пользователи по-прежнему смогут просматривать кэшированные данные.

И если говорить о доктрине, см. « Закон орудия ».

Яцек Кобус
источник
1

Doctrine - это уровень доступа к базе данных. Вы не хотите получить доступ к базе данных, но apis. Вы все еще можете создать Entity, но затем как простой объект, который не должен расширять нашу реализацию (popo). Он должен иметь репозиторий, который реализует все методы CRUD. В этом случае вызовы API вместо базы данных. Я бы создал интерфейс для этого. Приложение не должно чувствовать себя по-другому, за исключением того, что вы должны учитывать, что микросервис может не отвечать.

winkbrace
источник
0

К вашему сведению, теперь есть компонент HttpClient для v4.3.

https://symfony.com/blog/new-in-symfony-4-3-httpclient-component

Фред Флинт
источник
HttpClient - жуткая альтернатива, но этот ответ работает для меня, не думаю, что он заслуживает понижения, которое он получил.
Овен VII