I am designing a 3rd-party library for developers to retrieve data from various endpoints of another system which itself has multiple APIs. Each of those APIs may contain the same type of request but return different data, which correspond to a namespaced request class in my library:
- api1.domain/type1/schedule.json ->
Api1/Type1/ScheduleRequest
- api1.domain/type2/schedule.json ->
Api1/Type2/ScheduleRequest
- api2.domain/type3/schedule.json ->
Api2/Type3/ScheduleRequest
I created clients for every Api/Type combination, with specific methods for every single request:
$api1Type1Client = new APi1Type1Client();
$api1Type1Client->getSchedule(new Api1/Type1/ScheduleRequest());
$api1Type2Client = new APi1Type2Client();
$api1Type2Client->getSchedule(new Api1/Type2/ScheduleRequest());
These calls simply wrap a global request()
call in an AbstractClient class that they all extend from, so all of my request methods looks identical:
public function getSchedule(ScheduleRequest $request, array $config = []) {
return $this->request($request, $config);
}
This does have some benefits: IDE auto-completion, the ability to inject specific functionality per request if necessary, and enforcing type-hints. I made another attempt using somewhat of a Facade pattern, with a single ClientManager class, like so:
$cm = new ClientManager();
$response = $cm->getClient('api1.type1')->request(new Api1/Type1/ScheduleRequest());
This simplifies things a bit for the software developer as they only need to know about the manager, and if I ever modify the API later (namespace changes, etc.) it won't break earlier versions. I do lose the ability to inject functionality into specific requests, but in practice that hasn't been necessary, and if needed could probably take place in the Request class itself.
I made one attempt to simplify even further, and made a single Client that knows what to do based solely on the request class you pass to it:
$client = new Client();
$response = $client->request(new Api1/Type1/ScheduleRequest());
Here everything is abstracted out for the developer - they only know of one client, one request method, and then simply have to pass the request class they want to it and my library figures out the rest. So two questions, keeping in mind this is an ongoing 3rd-party library for other developers to use:
- Does it make more sense to have individual methods for all 250 of my requests, or a single
request()
that knows what to do based on the Request class passed to it? - Does it make more sense to have a individual clients for every Api/Type, a single Client manager, or a single Client?