Versioning assumes simultaneous existence of multiple (different) implementation of an artifact (code, document, etc). In the case of SOA, service versioning equates to coexistence of multiple versions of the same service, which allows each consumer to use the version that it is designed and tested for.
Multiple coexisting versions of the same service in the system allows for the independent life cycles of services and their consumers and minimizes the overall impact of the introduction of changes.
The basic idea of service versioning is fairly simple and straightforward, but its implementation requires definition of the following:
1. Units of versioning
2. Version Definition
3. Version Deployment/Access Approaches
Units of Versioning: Assuming WebService is implementation technology, Service as whole is versioned or individual methods in a service. Both approaches have their own drawbacks and benefits. The first approach is consistent with current Object Oriented and Component Based development paradigm but poses challenges when the service contain various methods and various users while change affects only few methods. The second approach has following advantages:
- It allows for immutable services. The service can provide additional methods (versions of methods), some of which can be deprecated over time, but the service itself, its name and classification, seldom changes. This scheme resembles versioning approaches in popular programming languages, such as Java or C#, where methods on classes are added and deprecated quite often while existing classes that are widely used rarely change.
- It minimizes the impact of the service changes to consumers. Only consumers using a particular method are affected by a change, instead of all service consumers.
- It minimizes the overall amount of deployed code. Only methods with a new version get redeployed in the process of introduction of the new version. The code implementing methods that have not changed remains unchanged.
The disadvantages are:
- It calls for deploying each method independently with its own endpoint address (es). (Although such a deployment approach is not mainstream today, it has some advantages, such as providing different service-level agreements (SLA) for different methods within the same service.)
- It requires a different, slightly more complex, service invocation-addressing schema. Instead of specifying a service that it needs to invoke, the service consumer, in this case, needs to explicitly specify the service, the operation, and the version of the operation that it requires.
Version Definition: The major components of a service are interface and message. All changes in interface and message definitions does not constitute a newer version, only those result in changes in consumer are marked as candidate for new version.
Service-Interface Changes: The service's method signature (interface) in case of semantic messaging is:
serviceMethod(XML in, XML out) Due to very definition of interface, it never changes, so no question of version. If each method is deployed and addressed individually, so new methods introduced will not impact existing consumers. If a method is deprecated, some sort of warning can be incorporated in returned message. In case of elimination, major impact in consumer and may require revamp of consumer.
Service-Message Changes: Message change will be covered by XML schema versioning technique. The changes in XML schema can categorized under three categories:
Revisions: It represents schema changes, which have no semantic meaning e.g. comments, formatting, white spaces, etc. So, no versioning effect.
In addition of this, initial incremental development of schema before publishing can be considered as revision. Minor Changes: It represents backward compatible changes. e.g.
a. Changing optionality of a local element or element reference from required to optional
b. Adding a global element or type
c. Adding optional elements to the existing type
d. Changing type of a global or local element to the new type, derived from the original by adding/restricting optional elements.
Major Changes: It represents non-backward-compatible changes to the document schema. e.g.
a. Changing the type of a local or global element to the new type, derived from the original by adding/restricting optional elements
b. Changing the optionality of a local element or element reference from optional to required
c. Adding or removing an enumeration value
d. Removing or renaming a global type or element.
Only major change in XML schema will constitute the versioning in service.
Implementation Changes: In Utopian world change in implementation of service will not constitute version but we are not. In reality, adherence to the interface does not constitute replace-ability of implementations. Replace-ability is not defined by interface alone, but instead by the contract—which includes interface, pre- and post-conditions, Quality of Service and certain SLAs—on which the service consumer relies. e.g.
a. New service implementation supports exactly the same interface (functionality), but changes timing (SLA). If a service consumer is invoking this service (method) synchronously, such change can significantly impact service consumer execution. As a result, service versioning might be required. (Interestingly enough, even redeployment of existing service, with changes to its implementation, can lead to the same results).
b. New service implementation supports the same interface, but changes incoming parameters validations (preconditions). In this case, some requests that had been processed successfully will now be rejected, requiring introduction of a new service.
c. New service implementation introduces a new security implementation (precondition), which often is not reflected in service interface, but done through the configuration settings. In this case, existing consumers will require sending security credentials; implementation change and creation of the new version is required.
3. Version Deployment/Access Approaches: There are two approaches for deploying versions of a service.
a. Covenant or Version Parameter: "if-then-else" like agreement where a single endpoint address for all versions of service (methods) but based on context based routing, incoming request is routed to appropriate version depending upon message embedded version information. This approach simplifies the addressing challenge but packaging multiple versions of same service in same packet is a challenge in itself. This approach is further abstracted using Mediator pattern where an external mediator will rout the incoming message as per embedded version information in message. The version details can be embedded either in namespace or as parameter in message.
Namespace example (Followed by Amazon):
Pros: Takes advantage of XML namespaces, works well with backwards incompatible schema changes.
Cons: Doesn't take advantage of XML schema extensibility.
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" >
< soap:Body> < xmlns="urn:company:service:v1" > 3.333333 < /Price > < /soap:Body >< /soap:Envelope > New Version
< soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" >
< soap:Body > < Price xmlns="urn:company:service:v2" > 3.33 < /Price > < /soap:Body > < /soap:Envelope >
Parameter example (in Header) --> Followed by eBay:
Pros: Uses XML schema extensibility.
Cons: Does not work well with incompatabile schema changes, doesn't take advantage of XML namespaces, requires users to pass the parameter in ever request.
< soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" >
< soap:Header > < Version xmlns="urn:company:service" > 340 < /Version > < /soap:Header > < soap:Body > .... < /soap:Body > < /soap:Envelope >
Parameter example (in Body):
< soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" >
< soap:Body > < SomeRequest xmlns="urn:company:service" > < Version > 340 < /Version > .... < /SomeRequest > < /soap:Body >< /soap:Envelope > a. Multiple End-point addresses: Every version is deployed separately and exposed to potential customer at different URI and port. This approach assumes that consumers will resolve the address of provider with help of some registry service (UDDI). This approach completely separate each version from each other and provide better scalability but requires complex addressing paradigm, and creates n number of services.
Example:
Services following this pattern: JIRA
Pros: Uses XML schema extensibility.
Cons: Requires users to update their invocation url each you update versions, Does not work well with incompatible schema changes, doesn't take advantage of XML namespaces.
As services get versioned, so the underlying infrastructure. It constitutes:
Ø Transport changes — e.g., switching transport from HTTP to HTTPS
Ø Message encoding changes — e.g., upgrading proprietary enveloping with SOAP.
Ø Changes in addressing schema— e.g., introduction of the WS-Addressing for reply-address specification
It is desirable to maintain backward compatibility but it might be technically not feasible or expensive. Facade pattern is one of the possible solutions.
Version Identification Strategies
Defining a schema is as good as defining a language for a special purpose. Version identification is done with a dot (.) separating major and minor versions ( major.minor). Traditionally major version refer to "incompatible" change while minor refers to "compatible" change. This is idealistic approach, in real life you will find several examples which are not in sync with this approach.
There are numerous variety of version identification strategy but following are few of the popular one.
A. All components in new namespace(s) for each version : version 1.0 consists of name space "a" and version 1.1 is of "b".
B. All new components in new namespace(s) for each compatible version : version 1.0 consists of namespaces "a " & "b"; version 1.1 consists of namespaces "a", "b" & "c"; version 2.0 consists of "d" & "e".
C. All new components in existing or new namespace(s) for each compatible version : version 1.0 consists of namespace(s) "a"; version 1.1 consists of namespaces "a"; version 2.0 and 2.1 consists of namespace "b".
D. All new components in existing or new namespaces(s) for each version and a version identifier : version 1.0 consists of namespace "a" and version attribute "x"; version 1.1 consists of namespace "b" and version attribute "y".
While deciding version strategy pay attention to:
- Supporting compatible evolution;
- Namespaces for identifying compatible components. Changing namespace names is typically is very invasive change;
- Tool limitation;
- Future direction.
Support for versioning in XML Schema
The simplest way of denoting versions in XML Schema is usage of an (optional) attribute at the xs:schema element - version. The content model permits Dewey notation of major.minor version numbers.
Since XML parsers are not required to validate instances using version, it is possible to implement custom representation of version, enabling the parser to include it in the validation process. This technique typically requires introduction of a versioning attribute as a fixed, required value for identifying a specific schema version. However, this approach for schema versioning is not very practical. There are several disadvantages:
- An XML instance will be unable to use multiple versions of a schema representation because versioning occurs at the schema's root.
- XML schema validation tools are not required to validate instances using the version attribute; the attribute is provided purely for documentation purposes and is not enforceable by XML parsers.
- Because XML parsers are not required to validate using the version attribute, additional custom processing (over and above parsing and validation) is required to ensure that the expected schema version(s) are being referenced by the instance.
- Marshaling/unmarshaling of XML document is very rarely done using direct manipulation of the DOM tree. The prevalent approach to marshaling is generation of the classes, supporting "automatic" marshaling, using tools like WSDL2Java, Castor, EMF, SDO, XSD, XSDObjectGenerator, and so on. In this case, classes are generated in the packages in Java or namespaces in C#, based on the schema namespaces, not the schema version.
Another option for denoting schema version is usage of XML Namespaces. In this approach, a new XML Namespace is used for all major version releases. This approach is well aligned with generation of marshaling/unmarshaling code by allowing generating code in different packages (namespaces), thus enabling a single service consumer to work with several major releases of schema simultaneously.
Yet another option is to keep XML Namespace values constant and add a special element for grouping custom extensions. This approach wraps extensions to the underlying vocabulary within a special extension element. Several industry-standard schemas favor this technique. For example, the Open Application Group's Business Object Documents (OAG BODs) include a
- It introduces significantly higher levels of complexity into the schema.
- It does not allow implementation of multiple extensions across different portions of the XML instance, because all extensions must be grouped within the extension "wrapper."
The most scalable approach to versioning of schemas is as follows:
- Componentization of the overall schema in logical partitions using multiple namespaces, thus containing changes.
- Defining a new namespace (reflecting the major version information) for every major version of each schema.
- Denoting every minor version as a schema version in a major version namespace. Because minor versions are backward compatible, generated marshaling/unmarshaling code also will be backward compatible.
0 comments:
Post a Comment