Versioning across Concepts

23rd of October 2017

Software throughout its lifespan is in a constant state of change. As it attempts to address the needs of its stakeholders, its functionalities and interfaces are adapted in smaller and greater ways. The impact of these changes varies as well, and can necessitate complete overhauls of dependent systems. This challenge has been constant throughout my consulting career, and started almost immediately with my project for bPost about master data management.

Where before the turn of the century the challenge of mitigating the impact was almost always met with change management and elaborate communication plans, the introduction of Enterprise Application Integration (EAI) and later on Service Oriented Architecture (SOA) made a more dependable solution necessary, and we began to look at technology (in the broadest of senses) to help us out. Design patterns were introduced, and frameworks built to tackle the issue.

 

Looking at the classic SOA paradigm, the books by Thomas Erl propose to use the namespace of the web service to incorporate versioning. This is a practice that has more than its fair share of traction in the development world. This namespace should be unique and is used in the SOAP binding:

<schema targetNamespace="http://example.com/2003/10/15/stockquote.xsd"
              xmlns="http://www.w3.org/2000/10/XMLSchema">
           <element name="TradePriceRequest">
              <complexType>
                  <all>
                      <element name="tickerSymbol" type="string"/>
                  </all>
              </complexType>
           </element>
           <element name="TradePriceResponse">
              <complexType>
                  <all>
                      <element name="price" type="float"/>
                  </all>
              </complexType>
           </element>
</schema>

There was a brief time during the hype of the Semantic Web, where the Universal Description, Discovery and Integration (UDDI) register would bring solace to this topic. Originally developed as part of the concept of service discovery, it tModel structure could not handle different versions of the same service, since the wsdl:portType is to be linked to a unique tModel. However, with some adaptations (for example IBM uses these in their WSRR implementation) this can become a possibility. However, with Semantic Web buried somewhere in an unmarked grave, UDDI soon followed suit.

UDDI Architecture Diagram
UDDI Architecture Diagram

This problem might seem less relevant in a Micro Services architecture, but even here versioning is needed when deploying what the evangelists dubbed ‘breaking changes’. Vijay Alagarasan, principal architect at Asurion even calls this an anti-pattern: Versioning Avoidance. The strategy needs to ensure that the new version is either transparent or that an easy migration is available for all consumers of your service. Sam Newman also offers a few suggestions in his seminal book on Micro Services.

Breaking Change in Micro Services

The issue with versioning for REST services (as they are the weapon of choice for Micro Services architectures) is that embedding the version into the URI is not in line with the idea of Hypertext As The Engine Of Application State (HATEOAS). It seems that there should always be a versionless URL that offers the latest incarnation of a service, as well as versioned URLs that either provide a service with reduced functionality or redirection information. The HTTP specification provides several options of which the following make the most sense in this context:

  • HTTP 301 - Moved permanently: indicating an obsolete version of the service.
  • HTTP 302 - Found: indicating that the service still exists, but on a different URL (which can be found in the location parameter of the Header).

Another possibility is (and one that is growing in popularity) is to provide the version in the accept-parameter of the HTTP Request Header. This is passed along to the server, that can in turn takes this into account and send back confirmation using the Content-Type parameter in the HTTP Response Header.

	Accept: application/xml;version=2

It becomes trickier when several types of components are present in the IT landscape, each with their own best practices on versioning. Especially if these components are linked into a coherent whole, like for example an automated business process that calls out to several different elements (such as a service task, a case, an incoming/outgoing message or a business rule).

Process Versioning with Different Versioned Elements

Most BPMS implementations have a proper way to manage version of the same process definition. For example, Camunda BPMS has a version tag that they add to the xml of the process definition. And all of these engines have the rule that any process instance that is launched, unless specifically stated, starts with the most recent version of the process. However, when deploying a new version, all instances remain in the old version for the remainder of their tasks. If a different need arises, the complex issues of process migration start to play a role. The question begs: Does the orchestrating component’s version number also augment each time one of the components has a breaking change or URL change?

		<bpmn2:process camunda:versionTag="1.5-patch2" ...

The main idea behind the breaking changes in the Micro Services paradigm is to avoid breaking changes if possible, and when it is not possible to implement these changes as soon as possible. In my experience, this also holds true for automated processes. However, the executable level of BPMN has the configurations of its outside calls (service tasks, events, cases, other processes) embedded in the process definition. This means that if your URL endpoint for your service changes, these configurations need to be adapted, prompting a new process definition version. Ideally, this should be avoided.

 

One of the ways to keep the impact out of the automated process, is to implement an abstraction layer for the outside calls. For example, in the configuration of the service task, to set an endpoint not to the actual service to be called, but rather to an application service (Thomas Erl terminology) that forwards this call to the relevant service. In the case the relevant service makes a breaking changes, the idea is to implement VETRO patterns into this application service to handle the breaking changes in a way transparent for the orchestrating process. A quick fix here would be to route the service call to the previous version of the service (as was intended at process design) as long as it remains active. A similar effect could be achieved by introducing a proxy that performs the URL rewrite to this previous version.

VETRO Design Pattern
 

Similarly, the events to be captured or sent out can be shielded from the process by using services that act as pollers/posters on messages queues instead of the process directly fetching the events from the relevant queue or pushing them onto the queue. This service would then have the VETRO patterns built into its Observer pattern (for example: the notify() method in Java). This becomes even more necessary if ObjectMessages are being used (which I personally would try to avoid as much as possible, preferring an Serialized payload instead).

When it comes to calls to other processes (even sub-processes) or cases, it usually becomes more tricky to decide on how to proceed. Even if the changes are not breaking, the underlying functionality of the process/case might have changed so that it is no longer compatible with the calling process. For this, a strategy where we always call the latest process/case is very ill-advised, and can lead to disruptions in the operational day-to-day process executions that could even hard to detect. Here, the case by case evaluation of the change will almost always necessitate either a new version of the process, or a continued use of the previous version of the process/case that is being called.

To be clear, this change in functionality can also happen for events and service calls, but there the frequency of such occurrences should be very low, as they are normally keyed to atomic functionalities that tend to remain constant during their lifecycle. For me, this means a quick check in the governance of the individual services is sufficient to alleviate the risk of such a non-breaking change in functionality.

Thought BPM SOA