5

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?

2
  • Who are the consumers of your API?
    – mmathis
    Jul 28 at 2:09
  • Customers of my organisation; it's a multi-tenanted SaaS application that manages payroll information, and some of these customers are multi-national with employees in both tax jurisdictions. The API is being used for integrations with their own applications (e.g. ERP systems) or other third-party applications. Jul 28 at 2:15

3 Answers 3

4

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 shouldn’t worry about the internal model of the back-end.

The solutions are very similar to what exist in a traditional ORM mapping, except that it’s 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 it’s 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 it’s only about tax code and tax scale, the aggregated API looks ok.

1

I would avoid using inheritance in REST APIs and use composition instead.

If I understand the matter correctly, TaxDetails consist of organization-defined fields and tax-authority-defined fields. This would be a natural choice for splitting into components. Within the part defined by the tax authority, use the names as they are given (subject to variable name constraints, of course). The organization-defined component can use organization-defined names, of course.

As a basic rule when dealing with multiple specific cases you need more than just one or two to generalize confidently. So don't overthink it before having more cases to cover, just implement a solution that's fitting now and is malleable enough. My gut feeling is that the composition approach could do it, but of course I didn't analyse the requirements deeply.

0

You can use abstract factory design pattern to achieve your result where you have factory of different tax detail and group abstract factory that will handle sub factory and generate different instance object base on which factory that is called

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.