Pergunta

Got a desktop application that interacts with some stateful third party web APIs. Its an interactive application, at some stages it has to halt execution and prompt for user input based on responses from said APIs etc..

Application core structure is as follows:

  1. Third Party API Service Class, my abstraction for handling the various REST API calls, transforming request / response data etc... The service class implements a generic Interface, to support replacing the third party service with a mock or different API. For example it could be IWebService which has four or five API calls.

  2. Core application, interacts with #1 via the generic interface, has the "core" application logic to call the API, get user input and so on.

For example (core would be something like):

// Transform API response to a generic set of of options
var options = apiService.Call1(param1, param2) 
// Ask user to select an option, and then call the API with the selection Option
var options2 = apiService.Call2(selectedOption) 
// And so on
.
.
.

As the APIs are stateful (there is a server side state at the other end and my responses must contain state tokens as well as other limited lifetime metadata).

This leads to strong sequential coupling, my API Service class cannot have its members invoked out of order.

It would be possible to make my API service stateless by having the calls return the state to the caller, and having the caller class pass the previous API state with the next API service call and so on.

However this breaks separation of concerns, my Core Application would then be aware of internal implementation details of this Service class.

So I'm faced with a conundrum:

  1. I keep sequential coupling, and rely on throwing exceptions at runtime for out of order calls
  2. I leak internal details (state of Service class) to my caller class and make the application strongly coupled to this particular implementation of the service.

I feel #1 is the acceptable route here as there is no getting away from the fact that the third party APIs have state and 2 is unnecessarily coupling my code.

Foi útil?

Solução

You can't really get rid of the sequential coupling between the methods of IWebService, because it is imposed upon you by the third-party API. You can either check at runtime if the developer has followed the documentation (your option 1), you arrange your code such that the compiler assists you in detecting attempts to make out-of-order calls, or you invert the dependency.

If you have Call1 return an opaque object that the main program needs to pass as an argument to Call2, then the application is not strongly coupled to the IWebService implementation. The application gets to know about an extra class, but that could just be declared as an interface without any members. Inside Call2, IWebService can just cast this argument back to the (hidden) internal type that it really is. This internal type can change as often as needed and the main application won't notice a thing, so the coupling is really low. The only thing you really do is make the sequential coupling very explicit.

As a third possibility, you can invert the whole logic. If the application always needs to make the same sequence of calls to the third-party API, while requesting more information from other sources in between, you can give IWebService just a single method to do the whole interaction with the third-party API. This method could accept one or more callback parameters that IWebService can use to ask the main application for more information when needed.

Outras dicas

I would switch out the apiService class and instead have a apiClient and apiClientFactory. That way you can store the state in the client.

var client = apiClientFactory.GetClient();
var options = client.GetOptions();
var selectedOption = ShowUIToSelectOption(options);
client.SetOption(selectedOption);
client.Dispose();

This allows your program to manage state using conventions that should be familiar to most programmers. Depending on your language, it may also offer an obvious and explicit means of defining the point of initialization (construction) and cleanup (disposal or finalization).

Licenciado em: CC-BY-SA com atribuição
scroll top