9

Say you have a web service, which adds business logic on top of a data source. What each API of this service pretty much looks like is - given a set of constraints, give me the items from the data source that satisfy these constraints. You can say you get a "view" of the data source back from the API.

Now, over time you get asked to return different kinds of views over the data source. You have the option to either add new APIs for each "sufficiently distinct" view, or to switch gears and provide a getFooDataView() API, which takes a parameter that specifies what kind of view you want. You have several competing pressures for deciding which way to go:

  • The existing big client of your service would prefer to be lazy, and not have to code up to new APIs when new views over the data are needed.
  • But, some of your request parameters (constraints) only make sense for some views, and not for others - you'd have to make your API contract looser by saying "well if you want XYZ view, setting the "foo" parameter will have no effect", with the unfortunate side effect that you can't make "foo" a required parameter even if it is so for some of the views.
  • It's becoming more and more the case that new clients want to leverage your service. You can't decide which would be more confusing to them - having to pick between different, but tighter-defined APIs, and one API where they have to know what combination of parameters really gives them what they want.

To distill this, when do you draw the line that something should be its own API as opposed to a variation of an existing API? Different people you have to work with have different views about what makes two client requests semantically distinct, so it can be hard to drive consensus about this matter. You also want to make sure that your service isn't prohibitively difficult for future clients to consume. What are some best practices arount making this kind of choice?

RuslanD
  • 721
  • 6
  • 18

1 Answers1

4

Personally I would lean towards smaller, tighter APIs. I don't think that it follows that having one one-size-fits-all API makes something simpler to use - in fact perhaps the converse since you need to figure out the right combination of loosely-typed "ju-ju" to get what you want whereas a tighter API can be made more self-explanatory. You are just hiding the complexity instead of making it explicit.

It's also likely that your one API will become ever more complex as it wraps more and more functionality, whereas the tighter APIs remain focussed and lean.

This smacks of the Interface Segregation Principle on a grander scale.

There are always other ways (such as good guidance) to help clients choose the right approach.

James World
  • 176
  • 3
  • I agree in principle, but how do you version your API? Your way, you either have to create new interfaces as functionality is added, or adapters. The OP's way, you can just add another "command" (the magic stringiness of that notwithstanding). It's the OP's API that will be tighter, leaner and more focused, not yours. – Robert Harvey Nov 20 '13 at 22:47
  • 1
    I don't follow your reasoning for the last sentence for the reasons I have given. For versioning, `SomeApiVersion1` and `SomeApiVersion2`... etc works wonders. This way you have total control over migration and deprecation. Multiple versions over the same interface looks good on Day 1, but you'll pay the price for a long long time. – James World Nov 20 '13 at 22:51
  • That's certainly a sensible approach, but there's no possible way that you can call it tight, lean or focused. You wind up with a proliferation of interfaces and adapters. – Robert Harvey Nov 20 '13 at 22:53
  • 2
    I am calling each API tight lean and focussed, not the whole thing. I am saying the complexity of the whole thing is constant whether you have a divided API or not, and that each client only has to take on the complexity they need with a divided approach. – James World Nov 20 '13 at 22:53
  • It's not constant... the overall surface area increases every time you version, unless you deprecate the older interfaces. It might be constant for the user, who doesn't have to maintain all those interfaces and their implementations, but it's certainly not constant for the software developer. – Robert Harvey Nov 20 '13 at 22:55
  • 2
    See, I believe that is a delusion (apologies for strong language). The complexity is still there, but you've just hidden it. I've seen that manifest time and time again in spiralling maintenance costs. It's a sure fire path to a big ball of mud architecture. – James World Nov 20 '13 at 22:56
  • And that doesn't happen with `SomeApiVersion1` and `SomeApiVersionN`? – Robert Harvey Nov 20 '13 at 22:58
  • let us [continue this discussion in chat](http://chat.stackexchange.com/rooms/11613/discussion-between-robert-harvey-and-james-world) – Robert Harvey Nov 20 '13 at 23:00
  • Concerning "You wind up with a proliferation of interfaces and adapters.": OP said that the APIs transport data from views. That indicates to me that there is a common data model below the views, database tables probably. As long as all views and the corresponding APIs are derived from such a common data model, the APIs will at least be less likely to contradict each other. A certain degree of overlap and proliferation is the necessary flipside of having specialized APIs. – Andreas Huppert Nov 20 '13 at 23:22
  • There are certainly advantages to segregating interfaces. On the other hand, the "chooser" approach is not without advantages. If implementations are expected to forward requests they don't understand to a particular static method associated with the interface, future version of the interface may add new actions without breaking existing implementations, provided that the new actions can be implemented (perhaps sub-optimally) in terms of existing ones. Further, option selectors can sometimes be very useful from a DRY perspective. For example... – supercat Feb 24 '14 at 01:45
  • ...if one has a `DoSmallOperation` method with a parameter which indicates whether its response to semi-expected problems should be to throw an exception or return `false`, a single `DoBigOperation` method may do likewise. If instead there had only been separate `DoSmallOperation` and `TrySmallOperation` methods, then there would have had to have been separate `DoBigOperation` and `TryBigOperation` methods. From a user-code perspective, it's better to have separate methods, but having them chain to another (also publicly available) version which combines both functions can avoid repetition. – supercat Feb 24 '14 at 01:48