0

I'm writing an integration with a 3rd party API Gateway, and in an effort to make it as decoupled as possible (and to be able to change the provider in the future), I created 3 interfaces that will contain methods for reading, writing and deleting from the gateway (because there are a lot of methods that I need to use so I don't want to cram everything in one large interface and violate interface segregation principle). The API Gateway is used to handle app creation and other CRUD operations.

And I'm not sure what is the best way to move forward. I can create an interface like this

interface Api_Gateway_Create {
  public function create_app( string $organization, string $developer_id, string $body );

  // other methods.
}

And then, when doing a concrete implementation, I will create a class that implements this, and when I need to use it, I need to provide the three arguments.

This seems a bit constricting. What if, when I replace the provider, I no longer need the $developer_id? I could set all the arguments with some default value

interface Api_Gateway_Create {
  public function create_app( string $organization, 
           string $developer_id = 'some-default-value', 
           string $body = 'some-default-value' );

  // other methods.
}

But that means that I'll end up with arguments I don't need potentially and that could mess up my implementation.

The last thing that came to mind is that I could just put a variadic, and then let the implementation take care of the arguments

interface Api_Gateway_Create {
  public function create_app( ...$arguments_list );

  // other methods.
}

In the implementation I'd have

class Create_App_Implementation1 implements Api_Gateway_Create {
  public function create_app( ...$arguments_list ) {
    list( $organization, $developer_id, $body ) = $arguments;
    // Some business logic.
  }
}

or

class Create_App_Implementation2 implements Api_Gateway_Create {
  public function create_app( ...$arguments_list ) {
    list( $organization, $app_key, $body ) = $arguments;
    // Some business logic.
  }
}

That way I don't need to care if my provider offers these arguments, because I'll just implement the ones I need.

This however presents another problem. In the consuming code, say a class that will use create_app() method via dependency injection, I need to make extra care that the correct values are passed. And this is not future proof, as I'd need to change the code in the consuming class as well (which to me seems like the opposite intention of the interface).

With first one I will never have to change the consuming code, because I don't care what provider I'm using, if I'm expecting the same result (based on the same input arguments). But as I've mentioned, this seems a bit constricting. Two providers could have different way of handling this.

Did anybody have to face this kind of thing and what is the industry standard of handling this?

I'm writing in PHP, but I think Java could also be suited because of its object oriented nature.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
dingo_d
  • 11,160
  • 11
  • 73
  • 132

1 Answers1

1

Did anybody have to face this kind of thing and what is the industry standard of handling this?

  1. I'm sure they have.
  2. There is no "industry standard" way of doing this.

(You should probably remove phrases like "industry standard" and "best practice" from your vocabulary. They are harmful for communication, in my opinion. Read "No best practices" and think about what he is saying.)


I'm not familiar enough with PHP to say what is commonly done to address this in that language.


In Java, there are two common approaches:

  1. Define lots of different method (or constructor) overloads for the common cases; e.g.

        public interface CreateAppApi {
            public String create_app(String organization, String developerId, 
                                     String body);
            public String create_app(String organization, String developerId);
            public String create_app(String developerId);
        }
    

    This doesn't work well if the parameters all have the same type: the different overload may not be distinguishable.

  2. Use the fluent builder pattern; e.g. define the interface like this:

        public interface CreateAppRequest {
            public CreateAppRequest organization(String value);
            public CreateAppRequest developer(String developerId);
            public CreateAppRequest body(String body);
            public String send();
        }
    

    and use it like this:

        String result = new CreateAppRequestImpl().organization(myOrg)
                              .developer(myId).body(someBody).send();
    

    This works nicely from the API user perspective, and it is easy to evolve. (Just add more methods for supplying new parameters to the API and implement them.) The downside is that there is more boilerplate code.

Java supports variadic parameter, but they are not suitable for the use-case you are describing. (That are applicable to cases where you have a variable number of values that essentially mean the same thing; e.g. an inline list of strings representing methods of our hypothetical "app".)


In Python, there is no method overloading ... because there is no need for it. Instead: - you can use positional or keyword arguments, or a combination of them. - you also have constructs like *args and **kwargs to pass through arbitrary parameters without explicitly declaring them.

Generally speaking, keyword parameters are better for APIs which require a variety of arguments with different meanings and (possibly) types.

So in this example you might write something like this:

class CreateAppRequest(SomeBaseClass):

    def __init__(self, organization='defaultOrg', developer=None, body='',
                 **kwargs):
        super(self, CreateAppRequest).__init__(**kwargs)
        if developer is None:
            raise Exception('developer is mandatory')
        self.developer = developer
        self.organization = organization
        self.body = body

    def send(self):
        ...

The builder / fluent approach can be used in Python too.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • To my knowledge I don't think that there is an overloading of methods in PHP like you described in Java. But the builder pattern looks promising. I could try to define certain getters that could be used in the implementation. Will have to think about it. Also, thanks for the link about the best practices, I've read the first part, looks interesting :) – dingo_d Aug 02 '19 at 14:29
  • I made it clear that I am NOT talking about PHP. You tagged the question with Java, and I am answering from that perspective. – Stephen C Aug 02 '19 at 14:31
  • Yeah, I understand, just was saying that I'm not sure about overloading. I can read Java code (having 0 Java skill), but they do behave differently, that I definitely understand :D – dingo_d Aug 03 '19 at 07:23