I'm working on a reusable client library to abstract a REST endpoint that we use in many of our in-house applications. It is actually split into 3 APIs, and certain endpoints/resources require different types of authentication (OAuth, JWT, and OAuth + additional BASIC). So to simplify all of that, I'd like to JAR up a client library and just have the app that's using it provide its credentials without having to worry about the HTTP calls.
How I would like the client API to work is something like the following:
OAuthClient apiClient = APICLient.getOAuthClient(oauth, credentials); // or OAuthClient apiClient = new OAuthClient(oauth, credentials), but see below JWTClient apiClient = APIClient.getJWTClient(jwt, credentials); AdminClient apiClient = APIClient.getAdminClient(oauth, and, basic, credentials);
I would prefer to use factory methods to instantiate the client based on the advantages listed in the first few items in the first chapter of Effective Java (name of method makes clear the arguments needed, not allowing creation of multiples, returning a superclass but instantiating a subclass), but that's not really my question.
The trickier part is that I have a ton of resources/endpoints that I need to abstract, so I would like to use an interface for each, rather than have the client class implement every single method. Interfaces would be like so:
CustomerResource searchCustomers(SearchQuery searchQuery) : List<Customer> getCustomer(int customerId) : Customer updateCustomer(Customer customer) : void removeCustomer(Customer customer) : void OrdersResource searchOrders(SearchQuery searchQuery) : List<Order> getOrder(int orderId) : Order etc... etc...
...as opposed to...
apiClient.searchCustomers(...); apiClient.getCustomer(...); apiClient.searchOrders(...); . . .
The resources would, however, require an instance of an APIClient to keep track of authentication tokens and expiration, so that renewal and auth headers can all happen in the background after initial setup. I think that's better than explicitly getting a token and having to pass it with every resource's method call as an argument. So I would like for the library user to get an instance of the interface through the client they created, like so:
CustomerResource cr = apiClient.createCustomerResource();
I'm struggling here because it feels like these XXXResource classes should be interfaces, but I don't want to have things like
interface CustomerResource public class CustomerResourceImpl
Because there's some code smell there. I could also just have my concrete APIClient classes implement all the resources and return this but then I've got a God class which is also an anti-pattern. Am I overthinking this, and should the resource classes just be concrete? It seems strange to have every one of them have the same constructor and see a bunch of these calls in the code:
CustomerResource cr = new CustomerResource(apiClient); OrderResource or = new OrderResource(apiClient);
Is there a good design pattern for this situation that I'm overlooking?