Service Contract Design

Services are different from libraries/frameworks in terms of its consumer's expectations from it. The libraries can be changed leaving the choice to its clients to use the new version or not. Between services and their clients this is not a straightforward option, it at all. Services and their client are bound to each other only at the runtime unlike libraries. A service contract cannot be designed like a library API.

A service contract consists of the network protocol, data format and data structure. Example of first two would be HTTP and JSON. The data structure is domain and organization specific. Some applications also use HTTP not just as network protocol but also as data. A common example is to use HTTP status codes for notifying errors, warning and status for a service request.

We have learnt the hard way[1] that why the services shouldn't be designed to appear as objects to the client. The distributed nature should be given its due while designing service contracts. Overall a good service contract design should decouple the client and the services in a way that:

- they do not assume anything about each other's programming or runtime platform

- the internals of the services are as opaque to the client as possible

- minimizes version upgrades onto its clients

Now lets look at some simple techniques which help achieving the above to some extent. We would start with an example and refine it as we go on. Our health information system dealing with mother and child health has ability to register beneficiaries into the system via following methods:

@WebService (URL = "/patients/register", Method = "post")

register(String name, int age, Date dateOfBirth, int numberOfChildren, Address address)

The client invokes this by passing a JSON to the http endpoint as:

{

"name": "Oejnhal Hathsu",

"age": "30",

"dateOfBirth": "15-08-2012",

"numberOfChildren": "1",

"address":{

"phoneNumber":"987654321",

"street":"20, GTH STEATR, PYAULI",

"city":"USELAHGID"

}

}

The client needs to know only the URL and this JSON structure to invoke the service. It is as simple as it can get except for one thing. Along with the data elements to send the client needs to know that address is sent as a nested object. There is no reason to impose this on the client, especially given that it doesn't add any value, and this can be changed to:

{

"name": "Oejnhal Hathsu",

"age": "30",

"dateOfBirth": "15-08-2012",

"numberOfChildren": "1",

"phoneNumber":"987654321",

"street":"20, GTH STEATR, PYAULI",

"city":"USELAHGID"

}

Hierarchy adds to number of things client knows for invoking the service and hence is bound to. Binding is bad for service contracts. The service would not be able to change the hierarchy easily even when they are not required or a different one is needed. For example: if we decide we do not want to capture street and city while registering we would be left with only phoneNumber inside the hierarchy. Flatter (less hierarchical) structures are better because because there is less penalty of getting things wrong.

So we have:

register(String name, int age, Date dateOfBirth, int numberOfChildren, long phoneNumber, String street, String city)

What happens when the data passed by the client doesn't match the data type on the service end? The worst thing would be if the client gets an error message from the JSON parser you are using. When there are multiple type errors in the request submission it is possible that your JSON parser doesn't report that and instead fails fast. This is not a good idea from the usability point of view though. If there are type errors along with other kind of errors like size validation, even a smarter JSON library wouldn't help. The idea of service is to serve, so when it comes to request validation done by the service failing fast is not a good idea. Instead providing as much information as possible to the client should be more important design principle. The technique which we can empl

register(String name, Number ageInMonths, Date dateOfBirth, Mother mother) -> for registering children

do not use exceptions

absence of a field or passing empty string in a request should not determine the which operation is invoked.

whether we register people as mother or child is not something which should be known to the client. they just come to the clinic to get some service.

Data consistency

Cross Application Code

Appropriate use of HTTP status codes

avoid schema validation, delay validation

do not share queues

avoid overloading instead the request should contain the variation. define your resources first.

liberal in what you take and conservative in what your respond with

Quite often enough attention is not paid to the service contract as much is paid to the

[1] Link given by Sriram in the forum.