Reputation: 2232
I’m going to create from scratch a PHP client for a REST API. I consider using Guzzle library.
I think the user shouldn't have to deal with transport things like request objects (even from custom xxxRequest classes) and should deal only with business objects / service classes. But may be I am wrong...
Here are some examples of possible architectures. Which respect good practices and why?
Feel free to improve them or suggest a better way.
Example 1:
class ApiClient {
function createBooking (BookingEntity $booking, CustomerEntity $customer) {
//...
}
}
This example allows the user to use only business object but the ApiClient class can start to be dirty if the API needs lot of methods... In addition the ApiClient has to be modified if we want to add features.
Example 2:
class ApiClient {
function execute(CreateBookingRequest $createBookingRequest) {
//...
}
}
class CreateBookingRequest {
private BookingEntity $booking;
private CustomerEntity $customer;
private $queryParams;
public createQueryParams() {
// to create the query params (from the attibutes) for the http client
}
}
Here if a new feature is needed the ApiClient has not to be modified, just a new class has to be created. However the user has to deal with classes named with Request suffix. IMO he has not to know anything about request or technical things.
Example 3:
class BookingService {
function createBooking (BookingEntity $booking, CustomerEntity $customer) {
//...
}
}
This example add one layer.
In all cases, where should be the Guzzle httpClient attribute, in the ApiClient/Service class? Or should the ApiClient class extend the Guzzle Client class?
Upvotes: 0
Views: 1496
Reputation: 1042
Well, there are no real standards for that out there, but this is definitely a hot topic, and I would even say, it's not Guzzle specific.
Short answer: I would choose between option 1 and 3 based on the complexity of the API.
Longer answer: If there are distinct resources (like booking for getting, creating, deleting; and a few more) then I would go with option 3. If there are only a few API calls then I would still go with option three, since it's a better representation from an API point of view (your API client is actually just an IMPLEMENTATION of the API, so it should follow the API's conventions, terminology...at least in theory), but option 1 might be faster and easier to understand at first. It's maintenance can get hard if the API gets more complex, so even then eventually you would have to rewrite it to option 3.
The configured Guzzle instance goes to the constructor of your class.
You can still have an API client class if you have Resource/Service classes, which in this case would not be more than a configuration entrypoint and a factory for your resource classes.
If you plan to reuse this client in multiple projects, you might want to consider using an HTTP Client abstraction (like HTTPlug instead of Guzzle, since you might not be able install Guzzle 6 in all of them (dependency conflict, etc)
Last, but not least you might want to check this out which is going to be an API foundation/bootstrap for exactly these cases (but it's not finished yet, so use it at your own risk)
Upvotes: 1