RESTful web services are extremely popular today as a mean to serve requests from web and mobile applications.
The “folder like” path structure of URLs is widely used to describe a hierarchy of resources, where usually the resource name appears in a plural form , followed by a resource unique identifier for a single resource, then a sub resource (in a plural form),a unique id for the sub resource and so on..
At times , the hierarchical structure of the resources needs to change. Adding a resource which is a child of the the last resource in the hierarchical structure is easy. But what happens if a resource should be added at the top of the hierarchy?
A concrete example to the above described problem:
Let’s say you are a Java developer, working with Spring boot (or Spring MVC) , and you develop a set of REST controllers for a zoo web application.
The suggested domain model includes the following resources:
1. The zoo itself as the root of the hierarchy.
2. Sections : these are areas in the zoo that contain several animal displays. Each section has a unique name and a set of coordinates that define its borders.
3. Animal display: an animal display is basically a set of animals from the same type (i.e: a display of lions)
4. Animals: a resource that describes each animal in a display
Your team discusses several approaches for API versioning , and decides to adopt a URL based versioning.
The team agrees the resource URLs will have a prefix of “/api/v1/zoo”.
The zoo resource has a single instance, as the web application serves a single zoo.
Some implementation details:
You decide to implement a base controller class, that among other things contains a constant that defines the base path:
You then create your first REST controller
Following the implementation of the Zoo controller, your team implements the rest of the controllers. The application is successfully deployed and launched, to the satisfaction of your customer.
The zoo is successful and now there are plans of expansion..
Your web application contributed to the success of the zoo.
The zoo’s management decides to open another location , in a different city, and plans to open a third location in two years.
There are even some conversations about opening several aquariums, one of them will be in the same city the web application was initially launched at.
You then realize, that if zoos were sub resources of a City resource, you would have had the required modeling.
As the structure of all the existing resources does not change, your team members wonder if there is a way to work on a single branch, and supporting the two hierarchies: one that starts with a city, and the other that starts with a zoo.
Time for a version bump..
As mentioned before, the team has decided to use URL-based API versioning.
As the hierarchy undergoes a significant change, it is worth considering a version bump. The old version, V1, will serve the old hierarchy (with zoo as the root), V2 will serve the new hierarchy (with city as the root)
As the data model does not change (except for introducing a city resource), your team wonders if the same controller classes can be utilized to serve the two versions.
You first change the BaseController, and introduce a V2 path prefix. V1 will eventually be retired.
Multi hierarchical controllers implementation
All the controllers that represent sub resources of the zoo will change , for example the Sections controller will look like this:
The resource identifiers for city and zoo are optional. They will be passed in calls from the V2 client, and will not be passed from the V1 client.
ZooResourceContext is a class that aggregates all the identifiers in the hierarchy down to the zoo resource, inclusive.
getContext is a method that returns a resource context based on the city and zoo resource Identifiers. If these identifiers are null, it will return a resource context for the first zoo that existed in the system.
The zoo controller, which used to be the root controller for the hierarchy is changed to:
In the above example there is no RequestMapping annotation on the class. The getZoos controller method is mapped only for V2, and the getZoo method is mapped for V1 and V2.
A few words about V1 cleanup…
Once V1 can be deprecated, it will be possible to change all the optional path variables to be required. In addition the getContext method will be redundant.
This article demonstrated how to manage two hierarchies using two API versions and optional path variables.
The pros of this approach is the fact it is possible to work on a single code base on the two hierarchies, and that it reduces code duplication and boilerplate code: no need to creates separate controllers per each version.
The con of this approach is that that the API structure becomes loose : it is technically possible to call V2 calls without specifying city and zoo resource identifiers. However, in this case, the “default zoo” which is the first zoo that was managed by the system will be returned.