Change is inevitable and interfaces (particularly of remote services) are no exception. Such interfaces go through various revisions for reasons such as enhanced features, bug fixes and so on. One must use versioning of type Major.Minor.Patch in order to avoid surprises to clients who have already integrated. Two tasks that vary at the interface layer based on versions are: (1) parsing the request parameters and (2) composing the response parameters back. What happens in between these two, the business logic processing, is not touched upon in this blog as it requires a separate discussion altogether. Also, this blog is focused on the code maintainability aspect only and does not get in to service routing concerns.
Service Framework owners have conventionally used multi-level inheritance as the class design to parse the request and compose the response parameters for each version. In the diagram below, every new version demands a new class that must be inherited from the class of the most recent version. Each class delegates to its immediate base class (the most recent version) after performing its own task, be it parsing the request or composing the response or both.
Though this design has been tested and proved for many years, it has this tiny hitch called multi-level inheritance. Is there a better alternative?
Better approach (Backward Chain of Responsibility)
One thing we can’t get away from in the conventional approach is the fact that new classes need to be created for every version, for the benefit of isolation. Having everything in one class makes it less modular and less cohesive. So, it is only the type of relationship that can be different from the conventional one (multi-level inheritance). An alternative could be a slight variation of the Gang of Four (GoF) behavioral pattern called Chain of Responsibility. I am calling it “Backward Chain of Responsibility” to emphasize the variation aspect.
The diagram below explains the design well, focusing just on the response composition task. You can also check out a sample application written to demonstrate this design here: RamkumarManavalan/APIVersioningClassDesign
As mentioned earlier, a new class is created for every new version. These classes inherit from a base concrete class that has the previousInChain member (of the same type). These classes are chained together through the previousInChain member in the super class. For instance, the previousInChain data member of version2 object points to version1. In other words, version1 is positioned before version2 in the chain. Each version class performs its task before calling super class’s method that in turn passes the control to the previous member in the chain.
Given a version number, you can choose to either start from the beginning of the chain or go directly to the member in the chain. For instance, if there are 10 versions and version eight (8) is asked for, you can either start from 10 and go on (where 10 and nine (9) would simply be passed up), or, you can directly start from eight (8). The choice really depends on the frequency of use of the individual versions. In the code example I wrote, I chose to write a factory that starts from the corresponding version class directly, for performance reasons.
Where this design fairs better than the conventional approach is in the absence of multi-level hierarchy. The hierarchy level is fixed to one (1) through the use of delegation/composition.
Sample code: RamkumarManavalan/APIVersioningClassDesign
A discussion with my colleagues on this topic yielded Decorator chain as an alternative. Though it solves the problem, I am still with Chain of Responsibility because each member in the chain (versioning) is better executed in some fixed order (upward or downward), whereas in a decorator, the order does not really matter. Also, decorator works on something “core” which is really not present in the versioning use case.
Courtesy: the diagrams have been created using an application that uses plantUML library.