Platinum Partner
java,enterprise-integration,spring mvc,spring,json,rest,jackson

REST-JSON Service Versioning – Simple approach

Problem Statement:

As a provider of a service, one is obliged to maintain different versions of a service. One of the possible reasons could be the existing consumers of the service doesn’t want to upgrade to newer version.

Given the above problem statement, the service provider would need to manage & maintain multiple execution path, Request & Response structure / objects.

Note: I have chosen Spring MVC + Jackson Library as software stack for the solution approach.

Ex:

1.  Service that returns an employee object, given an employee Id.

@RequestMapping(value="/employee/{id}", method=RequestMethod.GET)
public ResponseEntity<Employee> fetchEmployee(@PathVariable int id){

   //Business operation/search & employee object is responded

   return new ResponseEntity<Employee>(new Employee(), HttpStatus.OK);
}

2.  The Employee response object could look something like this,


public class Employee { private int employeeId; private String name; private long primaryNumber; public int getEmployeeId() { return employeeId; } public String getName() { return name; } public long getPrimaryNumber() { return primaryNumber; } setters... }

3.  Assume the above service (/employee/{id}) is of version 1.0 & you are expected to add a secondary number as part of the employee response object only for a specific set of consumers. This would mean the service will have two version (1.0 & 2.0) 1.0 without secondary number & 2.0 with secondary number part of the response.

4.  Typical nature of implementation is write another service & expose that to the new consumer, this would also mean duplication of request/response objects in this case employee class.

Solution:

Based on the problem statement the solution proposed below would introduce a seamless versioning of your REST services on JSON.

I have introduced a new type of annotation to indicate all the supported versions of a REST Service.

@Version(versions="1.0,2.0,3.0...")

1.  Service that needs to be version aware, will be annotated with the custom annotation as show below.

@RequestMapping(value="/employee/{id}", method=RequestMethod.GET)
@Version(versions="1.0,2.0")
public ResponseEntity<Employee> fetchEmployee(@PathVariable int id){

	//Business operation/search employee object is responded

        return new ResponseEntity<Employee>(new Employee(), HttpStatus.OK);
}

2.  It’s just one part of it, how to control the request/response object, by version?

I have introduced a custom annotation @Include & @Exclude, to indicate if a property in the JSON object needs to be included


@Include (versions="1.0,..") - Include property for given versions of the service
@Exclude (versions="1.0,..") - Exclude property for given versions of the service

public class Employee {

private int employeeId;
private String name;
private long primaryNumber;
private long secondaryNUmber;

	@Include (versions="2.0")
	public long getSecondaryNUmber () {
		return secondaryNUmber;
	}

	
	public int getEmployeeId() {
		return employeeId;
	}

	public String getName() {
		return name;
	}

	public long getPrimaryNumber() {
		return primaryNumber;
	}
	
}

3.  Part of the solution is to overwrite JsonSerialize & JsonDeserialize to read the appropriate tags above to interpret the versions & form the request / response.

4.  Now Consumer has the responsibility to communicate the version of interest, which will be passed as request header attribute “version:1.0” or “version:2.0” etc.

Conclusion:

I have a working solution that I am thinking of open sourcing in a week or two, this article is more to get a feedback on the problem statement & your thoughts on my solution approach.

PS:

1.  Similar approach can be used for REST service on XML (with java Bindings) with some overwrites on XML serialize & de-serialize.

{{ tag }}, {{tag}},

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}
{{ parent.authors[0].realName || parent.author}}

{{ parent.authors[0].tagline || parent.tagline }}

{{ parent.views }} ViewsClicks
Tweet

{{parent.nComments}}