Principles of Service Design: Program to an Interface

I’ve been thinking a fair amount about service design recently, and one of the
things that I’ve found difficult is deciding how to implement and version a
service in such a way that makes it pretty trivial to support (or not) older
versions of that service. Turns out that advice which was meant to be for
writing code works for writing services too viz. Program to an Interface.

Program to an Interface

Borrowing from a lot of blogs and books, I’ve decided that viewing a service
interface in the same way I’d view a programming interface will help me.
Interfaces can be versioned, they isolate the client code from the
implemention of the interface, and versions should be immutable once
published.

It is desireable that a service hide implementation details so that if, for
example, a database table changes inside an application which is providing a
service, the clients of that service should not have to change.

Much like interfaces in a programming language, by specifying a well known
interface to the service we free ourselves from having to worry about how
clients of our service interact with the service, so whenever we need to
change the implementation of our service we can do so without worry, and
vice-versa; clients of our service do not need to worry about the service
implementation changing as long as the interface remains consistent.

Of course, interfaces may have to change as we try to support new
functionality, and when they do change we want to be confident that we’re
using the correct version of the interface; just because v2 has been
released, that doesn’t mean our clients automatically support it. We want to
continue using v1 until we get a chance to update our a client code.
Interfaces should be versioned to give us that choice.

When we think of a service we generally think of a web service and in these
heady ReSTful days the interface is generally though to be the combination of
URIs that we interact with. That’s not strictly correct. Supporting an
interface does not just mean that your URIs are manageg between service
versions, it also means that the content returned from service calls (ie
HTTP requests).

Of course, a web service is only one kind of service; it’s quite possible to
have services that do not have a web interface, most usually in situations
where processing not suitable for synchronous request-response style messaging
that is usually provided by a web services, situations such as order
processing or inventory handling, where we may take several seconds to confirm
an order is valid and paid for, that there is stock and that the order meets
some anti-fraud criterea which we have defined. The interface to these
services should also be versioned for the same reasons as as web service
should be.

The version of the interface we are conforming to should be detectable with
each message passed or sent, no matter the transport or service type used; we
should know that we’re dealing with version 3 of an API and not just that
we’re talking what might be some version of the API and trying to guess which
one to use.

Handily, versioning interfaces for these types of services is pretty much the
ideal use for a mime-type, and most message transports support setting custom
mime-types:

Mime types have a space reserved for vendor specific types, application/vnd,
inside which we are free to define our own types, although thre are a few
conventions which are useful to make sure we do not have name collisions.
Name your mime types such that they to include your organisation name, a very
short - one or two words - description of what they’re representing, a version
number, and a base format.

For example, say you worked at the Acme Toy Company, and when your web service
accepted an order via its ReSTful interface it would put on a message queue a
message with four fields, customer_id, purchase_order_id, amount and
description, in a JSON representation:

We could coin a mime-type, application/vnd.acme.order-v1+json, and publish
an interface specification that says any message claiming to be
application/vnd.acme.order-v1+json would have these four fields. Then in a
consumer of the orders message queue we could use the Selective Consumer
pattern to subscribe to the queue asking for just messages of this mime-type.
Inside the consumer we can now be fairly confident that we will deal with only
orders which we know the format of and can process. We could have parters POST
messages with this mime type in the Content-Type header so we know we’re
talking about the same thing all the way through the system.

A few months pass, several partners are using the order API, but we want to
automate our stock inventory systems, so we decide that instead of a plain
text description we want item ids. We don’t want to force this change on our
partners however, because their development cycle is slow and they’re sending
us loads of orders - we like their cash.

We can now publish a second version of our order interface,
application/vnd.acme.order-v2+json, which defines messages which look like
this:

It’s now pretty trivial to add a second Selective Consumer which consumes
only application/vnd.acme.order-v2+json messages and updates our inventory
accordingly. There’s no reason that the v1 conumer can’t keep running and
working with v1 orders, and there’s a smooth and unhurried migration path for
our clients from version 1 to version 2 of our API. We can easily support both
versions or reject older versions as we choose. We could even use some
combination of Splitter, Translator and Enricher to route the
v2 message into the consumer for the v1 messages whie splitting off the
inventory management messages and routing them to a tiny consumer elsewhere.
None of this matters to our partner however, because they know that they’re
working to the interface we’ve defined.

We may eventually decide that the ReSTful order service is not appropriate for
v3 of our service; perhaps we bought over by web 2.0 hip kids and want to use
WebSockets because hey, those are cool, right? When we receive an order which
claims to be v1 or v2 on the ReSTful service we can still happily accept it,
if we receive anything else we can return an HTTP 406 to tell the client
that we can’t accept orders that way for v3.

In contrast, if we had used application/json we’d have to do some sort of
guessing based on the message fields we were passed to know which version of
the service the client wanted to us. This would be just about practical with
the trivial example above, but it’s messy, and once there are several versions
of the service interface it becomes much harder.