11

We are building a Microservice architecture for our projects, with mostly front-end Symfony applications interacting with back-end RESTful APIs.

The problem is this approach is breaking the Symfony entity management relying heavily on Doctrine with database. Where Symfony usually handle entities with Doctrine, automating most of the work, this cannot be reproduced easily when we have to access external data from the APIs.

For example, with a Client entity:

  • Using Doctrine, we just have to define our Client class, and it is now easy to create, update, retrieve our clients
  • Using the REST API approach, clients are accessible through the API, but we have a lot of work to define how the client is created (POST), updated (PUT), retrieved (GET), etc.

To be noted clients are used by several applications, not only the front-end app, hence the dedicated API.

Should we create classes with entity-like methods hiding the API calls complexity, importing all the API data locally and access them through Doctrine, or any other way?

3
  • I'm in the same boat as you are. Consuming external API's with clients generated from OpenApi / Swagger specifications. Wondering about best practices for consumption 'life cycle', crud operations, parameters and filter form generation. Currently expanding my search to include any approaches, regardless whether it's Symfony specific or not.
    – upstream
    Commented Dec 9, 2016 at 15:41
  • Having worked on this problem for several months and coming back on this question, both answers so far provide a similar solution: abstracting api calls with popo. That's the way we ended-up using, though other solutions exists. In similar Webapp <> API communications contexts, using abstraction level hiding API calls from the Webapp seems a good solution. With the rise of microservices and API-lead approaches, no doubt related best practices and tools will emerge to solve what appears to be a common issue.
    – Pierre B.
    Commented Jun 18, 2017 at 14:48
  • Over here a similar approach was applied. Business logic is now contained in an 'action' layer, which doesn't care if it's the REST api or a cli command calling it. Alistair Cockburn's hexagonal design was a great starting point in our case: alistair.cockburn.us/Hexagonal+architecture
    – upstream
    Commented Jun 20, 2017 at 8:25

3 Answers 3

2

I've made a symfony-based project that is using an external API (JSON); what I did was creating an independent client library ("client library" - a piece of software, composer package), with it's own set of entities (POPOs); it integrates with the framework using interfaces provided by Symfony (for example, by simply creating a custom user provider).

The client makes http calls "behind-the-scenes" - that is important for future testing capabilities. You don't want to expose the way you communicate with your data source and also you don't want your tests to rely on live api.

Client library interface (example how it may look like):

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);
}

The client library internally uses Guzzle for communication and Doctrine Cache component for caching the results. The mapping between entity objects and json was made by mappers, that once written - did not change very often (or event at all). In this case I would suggest using the JMS Serializer for an automated transformation to and from JSON (I assume that you use JSON).

You will need a good caching mechanism and local storage, like Redis. Making api-calls on each app request will kill your server and drastically slow down your application. It's very important to understand how http caches work. If your api does not use caching headers (or uses it in an obscure way) it will be very difficult and resource-consuming to keep track of the changes.

You will also want to think about how the client should behave if the connection breaks - should the client use stalled data? It would be a good idea to use some proxy server between your app and the API. In this case the proxy (like Varnish) could speed up your requests and also refresh stalled data in the background without slowing down your app. It will also keep your website online in case of an API failure. You might not be able to write data in the meantime, but your users will still be able to browse cached data.

And speaking of Doctrine, see the "Law of the instrument".

1

Doctrine is a database access layer. You don't want to access a database, but apis. You can still create an Entity, but then as a simple object that doesn't have to extend our implement anything (a popo). It should have a Repository that implements all the CRUD methods. In this case calls to the API instead of the database. I'd create an interface for that. It doesn't have to feel different for your application to use, except that you must take into account everywhere that a micro service might not respond.

0

FYI, there is now an HttpClient component for v4.3.

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

1
  • HttpClient is a guzzle alternative, but this answer works for me, don't think it deserves the downvote it got.
    – Aries
    Commented Jul 15, 2019 at 8:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.