Software Engineering
api-design web-api internationalization
Updated Fri, 16 Sep 2022 12:33:09 GMT

Designing an API for international markets


I am working on a REST API for a company that, until recently, has been focused on the Australian market. The result of this is that the API has resources and fields which model the Australian market, especially in relation to taxation, superannuation etc. We are now looking to move into overseas markets, starting with New Zealand.

Obviously there will be some degree of overlap between the two, but how do people go about handling the differences in terminologies and concepts, and how best to switch between them? For instance, the API might return a list of employees employed by a company; some of those employees are based in Australia, and some in New Zealand. An employee's tax details will have a unique identifier provided by the relevant taxation office (ATO in Australia, IRD in New Zealand), a basic salary, a contracted weekly number of hours, and a default taxation code (a Tax Code in Australia, and a Tax Scale in New Zealand).

How would you go about surfacing that information in a REST API? I want to avoid an EAV approach as much as possible; does that mean I need to go for some sort of inheritance-based model?

e.g.

public abstract class TaxDetails {
  public string EmployeeReference { get;set; }
  public decimal BasicSalary { get;set; }
  public decimal HoursPerWeek { get;set; }
}
public class AustraliaTaxDetails : TaxDetails {
  public string TaxCode { get;set; }
}
public class NewZealandTaxDetails : TaxDetails {
  public string TaxScale { get;set; }
}

or do I keep a "simpler" model and just not set values that don't apply in one region, and let the serialiser tidy up the model? e.g.

public class TaxDetails {
  public string EmployeeReference { get;set; }
  public decimal BasicSalary { get;set; }
  public decimal HoursPerWeek { get;set; }
  public string? TaxScale { get;set; }
  public string? TaxCode { get;set; }
}

A third option would be similar to the second, but try to find a neutral term for fields which are semantically the same but just different in naming (such as TaxScale vs TaxCode). It feels like the worst of the solutions (especially because naming things is hard), and you still have to deal with cases where there are no direct equivalents between two different regions; and it will scale even worse when you start to consider more than two regions...

So. How do you good folks handle this sort of scenario?




Solution

According to your narrative, you have indeed a common model with several country-specific specializations. This situation corresponds in principle to inheritance.

But a REST API is something that you have to design without inheritance in mind, because for the client apps the question is how to consume a published API. It shouldnt worry about the internal model of the back-end.

The solutions are very similar to what exist in a traditional ORM mapping, except that its for API mapping:

  • you may provide specialized endpoints for a tailored country-specific API. This is the cleanest approach and simplest to consume (in ORM this would correspond to the concrete table inheritance). It might however be very cumbersome if you have many small variations.
  • you may put all the fields in your API and use only the relevant ones (in ORM it corresponds to single table inheritance) But please document clearly (having consumed such APIs I can say that its often a guesswork to figure out what field can be used in what circumstances). This is not in the spirit of the interface segregation principle, but it is very practical, especially if the difference is only about a few fields.
  • in theory you may also offer a base interface and let the client compose the elements by calling some additional endpoint to get the specifics (this corresponds to the class table inheritance in ORM). But while it nicely implements the composition over inheritance principle, it is more difficult to consume. Keep this approach for the more complex models where none of the former offer a satisfactory answer. (e.g if it would not just be about a tax code or a tax scale but a complete tax scheme with many more details and rules).

So if its only about tax code and tax scale, the aggregated API looks ok.