A few days ago, my BBF (Big Boss Forever) Vijay R asked the following question:
Any resources on how to map OO design (controlled state change via methods) to RESTful services? #help
— Vijay Ramachandran (@vijay750) October 24, 2013
Here’s what I think about it. There are a few things that are very different about designing HTTP APIs as compared to language-native implementation design:
- The goal of an HTTP API is to minimise coupling and facilitate interoperability, which is less of a concern when the usage environment is restricted to a single programming language and its runtime
- HTTP APIs must be designed to maximise stability and minimise change over time. On the other hand, implementations must be designed to support business evolution.
- Implementation design often has a lot of artefacts that result from addressing the constraints of the implementation environment (e.g. GoF Patterns) whereas HTTP APIs usually don’t have a target environment to deal with.
Given the above differences, it’s clear that a 1:1 mapping between a language-native API to an HTTP API will be sub-optimal, unless there is an adapter interface that is designed with the intent of language independent export. The above list of priorities gives some guidelines about how to design such an interface:
- Exchange Plain Old Datatypes or something like C-Structs. This comes from the study of coupling characteristics of software components, which indicates that Data Coupling is the most desirable, low level of coupling. Data Coupling, by definition, involves exchange of Plain Old Datatypes.
- Provide Coarse-grained Methods that exchange a minimal data “representation” of business resources. Give the clients basic CRUD APIs and hide all validation and business logic behind their implementation. The client should only have to deal with what data they want to save or retrieve, with minimal knowledge of how it is done, other than the rules of business that the client has the liberty to select. This is the essence of Representational State Transfer (REST, yes, that one).
- Use Ubiquitous Protocols and Wire Formats since we don’t know whether our API will be consumed by tunnelling middleware (proxies, translators, etc.), dumb end-points (archivers), smart end-points, humans, or even monkeys, perhaps.
Following the above thought process, it is clear that seeking to directly map a language-native OO interface will result in something more like RPC than a REST interface. There is nothing wrong with RPC – it’s just a different (tighter) level of coupling compared to REST. Whether to directly map the OO API to RPC end-points or provide a more loose data level coupling (or something in-between) then becomes a question of intent – who are we writing the API for.
After serving the role of an API provider for several years in various projects, I spent the last few years as the consumer of a third party API. I’ve come to believe strongly in the above 3 design principles. A data oriented API with few, uniformly designed methods is the easiest to work with. Most importantly, though, the worst pain that an API provider can inflict on a client is to make them update their code.