1

I am trying to develop an API that tries to integrate features of various service providers. I am at the phase of developing a model(class structure that identifies with the context of domain) that helps in unifying the fields for data that gets sent out to different service providers.

The problem I am trying to solve is to choose whether I consolidate fields from different service providers to create a model which is completely generic or have a model with common fields alongside provider specific fields.

Eg: I have a car rental API which tries to interface with multiple vendors. Each vendor has an API which lets me search of cars in an area with-in a car category. But each vendor-api has it's own added unique capabilities which separate them from their competition.

When I start out integrating with different vendors, I would like my API to do just the basic feature search - for which I can identify fields required for the common model. But as my API evolves I want to expose vendor-unique search parameters through my API. When I am in the process of expanding the features, do I separate fields related to them into different classes or compose them in existing model or do i add those fields to the common model and interpret unique fields based on the context available from the data.

Approach 1 :

CarReservation{

  String userName;    
  String id;
  // in millis
  long startDate;
  long endDate;

  CarCategory carCategory;

   //--end common model
   // only vendor 1 supports discounts on referrals 
   Referral referral;

   // only vendor 2 supports delivering and pick up from home
   Address homeDeliveryAddress; 
    ...    
 }

Approach 2 :

CarReservation{

  String userName;    
  String id;
  // in millis
  long startDate;    
  long endDate;

  CarCategory carCategory;

  //--end common model

  vendor_1_name : {
  // only vendor 1 supports discounts on referrals
    Referral referral;      
  }

  vendor_2_name : {  
    // only vendor 2 supports delivering and pick up from home
    Address homeDeliveryAddress;        
  }
..    
}

The advantage of using single common model(Approach 1) is it's cleaner, but over time as more vendors get added with very specific functionality - the model gets bloated with too many ungrouped fields. I understand we can organize the model better with a hierarchy even with many fields.

The advantage of using separate composite model classes(Approach 2) for different vendors is that it would help us group fields better and allow for isolating changes required in interpreting the data. But downside is that it exposes vendor specific fields.

Please let me know if the direction in which I'm heading is the right one or if there is another/several other approaches that I should be considering which would help with adding more features into the API. Let me know if you need further details/information related to the question.

PS: The sample model(CarReservation) I mentioned in the example is something I haven't thought about too much. I am actually modeling in a different domain, details of which I cannot disclose. :)

1

We can invert the composition.

Right now, as you have commented, our business is to provide a unified interface. For brevity, let's call our business CarReservation.

Our CarReservation is the crosscutting concept among the 3rd party APIs. So instead of coupling our model to their model, we couple their model to ours.1

Right now, the CarReservation is a hub of random features whom, in most of the cases, will remain empty a high number of them. It will lead us to check against these features over and over in order to determine if they were implemented or not (obj.getReferral()!=null) and to change the model1 every time a new feature is adopted. At the early stages of the development, it might seem irrelevant, but once in production, to change the CarReservation might lead us to make hard decisions. For instance, how to keep it backwards compatible. This risk is present no matter what we change. However, we can narrow down the impact of the changes.

Inverting the composition, we get something like this

class Referral{
   ...
   private CarReservation reservation;
} 

So, every time a new feature is adopted, we don't have to change CarReservation.

class Delivery{
   private DeliveryAddress address;
   private CarReservation reservation;
 }

Both Delivery2 and Referral 2 may contain specific vendor's attributes. Two vendors may understand Referral in different ways. So if Referral differs from one provider to another, we can implement the differences within a specific model1. More or less, each feature could be a bounded context itself, with its own complexity. It will take an exercise of abstraction but our core (CarReservation) will remain the same.

Later, we can tie the related features alongside with the CarReservation within a DTO where CarReservation and Referral are at the same level, instead of nesting.

Of course, It doesn't prevent us from also maintaining the code. The more features we want to adopt, the more components we get. But how many of them we adopt is part of our strategy. Probably, we won't adopt every single feature from every single provider, we will adopt those that add value to our business.

As usual, a more dynamic approach is possible. For instance, implementing reflection (if our language supports reflection) but I consider this approach to be far more abstract and hard to deal with.

Another possible approach is implementing EAV which makes our model very extensible, but hard to query on. However, it worth a mention.

In any case, our system works as a facade. It should hide 3rd party API details for the sake of the facade. So if we have to expose these details we should do It in our own terms and conditions, otherwise, the external APIs will constrain -eventually- the way our system evolves.


1: Here model, domain or business are interchangables. For simplicity, I have choosen model

2: Don't you pay too much attention to the names. I haven't thought about them too much.

  • Thank you Laiv, your answer gives me a direction to think about. I agree with you that the model we expose should be interpreted and translated to map to vendor specific APIs. – pavan kumar chaitanya Jul 24 '17 at 20:39
  • To my experience with this sort of integrations. Is very important to don't miss your business, pull these features that contribute to make better and valuable your business. Don't mimic very single API provider. – Laiv Jul 24 '17 at 20:44
  • Inverting composition would make us add more API end points as the model would change. I would be looking to stick to one model and one API end point and still have some flexibility adding changes. I would also want to keep my model as descriptive as possible, thus using reflection to determine fields is a path I prefer to not take. Your reply is highly appreciated and I like the direction in which the though process is heading. While I am thinking on these lines, I would like to hear if we can take any other approach which would help me stick to the one API end point and one model as input. – pavan kumar chaitanya Jul 25 '17 at 0:01
  • I agreed with not taking the path of reflection. I'm not a big fan of reflection either. Regarding the unique endpoint, The model suggestion was addressed to the domain, not the API itself. The API is meant to be one more abstraction layer which could map/hide the domain complexity. Consider having different models: 1 physical (3rd Party API o persisted), 2. Domain data model (where your business lives at) 3. Public (API Rest). Look at it from this point of view. You domain hides 3rd party API complexity as much as your API rest hides your domain model. – Laiv Jul 25 '17 at 14:53
  • Thank you for the explanation. It gives me a direction and insight into how I would like to model and design the API. – pavan kumar chaitanya Jul 26 '17 at 16:46
0

If you go with approach #1, you can add new vendors without the clients needing to update. If you internally handle the mapping from common fields to vendor specific fields you can easily change those mapping internally without changing the interface for your API.

If you go with approach #2, you don't get the same benefits as above. Each new vendor is going to cause you to keep bumping your API version and your clients will need to keep up. Also, if a vendor drastically changes their API then this might cause you to make similar changes to your API which is a headache to deal with and a sign of not so good design.

Personally I think that an API is a level of abstraction that is supposed to make life simple and easy. If you're just creating an indirect way to access another API then what's the point?

  • I'm asking for a recommendation from a maintainability perspective. As mentioned by you, I would be doing a mapping internally from the fields in the model to vendor specific APIs regardless of which approach I take. Also, the purpose of this API here is to provide a unified interface to multiple APIs from different vendors. I hope the information helps you to answer the question. – pavan kumar chaitanya Jul 22 '17 at 2:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.