Patterns of Change: Part 2, a detailed example : a perfect world spoiled by reality



Patterns of Change: Part 2, a detailed example

In this article, I follow up the discussion started in the first part with a concrete example.

Let's suppose we are creating a new blogger api using the command value object approach.

Step 1. Define the abstract command pattern using XML Schema.  This will define the basic commands your Web service will support.

This blog API will support three basic commands: 1) Post, 2) Update and 3) Delete.
Command.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema 
  targetNamespace="CommandPattern" 
  xmlns:xs="http://www.w3.org/2001/XMLSchema" 
  xmlns:cp="CommandPattern" 
  elementFormDefault="qualified" 
  attributeFormDefault="unqualified">
  <xs:complexType name="CommandType"/>
  <xs:complexType name="CommandResponseType">
    <xs:attribute name="Status" use="required">
      <xs:simpleType>
        <xs:restriction base="xs:NMTOKEN">
          <xs:enumeration value="OK"/>
          <xs:enumeration value="FAIL"/>
        </xs:restriction>
      </xs:simpleType>
    </xs:attribute>
  </xs:complexType>
  <xs:complexType name="PostCommandType">
    <xs:complexContent>
      <xs:extension base="cp:CommandType"/>
    </xs:complexContent>
  </xs:complexType>
  <xs:complexType name="UpdateCommandType">
    <xs:complexContent>
      <xs:extension base="cp:CommandType"/>
    </xs:complexContent>
  </xs:complexType>
  <xs:complexType name="DeleteCommandType">
    <xs:complexContent>
      <xs:extension base="cp:CommandType"/>
    </xs:complexContent>
  </xs:complexType>
  <xs:complexType name="PostCommandResponseType">
    <xs:complexContent>
      <xs:extension base="cp:CommandResponseType"/>
    </xs:complexContent>
  </xs:complexType>
  <xs:complexType name="UpdateCommandResponseType">
    <xs:complexContent>
      <xs:extension base="cp:CommandResponseType"/>
    </xs:complexContent>
  </xs:complexType>
  <xs:complexType name="DeleteCommandResponseType">
    <xs:complexContent>
      <xs:extension base="cp:CommandResponseType"/>
    </xs:complexContent>
  </xs:complexType>
  <xs:element 
    name="PostCommand" 
    type="cp:PostCommandType" 
    abstract="true"/>
  <xs:element 
    name="UpdateCommand" 
    type="cp:UpdateCommandType" 
    abstract="true"/>
  <xs:element 
    name="DeleteCommand" 
    type="cp:DeleteCommandType" 
    abstract="true"/>
  <xs:element 
    name="PostCommandResponse" 
    type="cp:CommandResponseType" 
    abstract="true"/>
  <xs:element 
    name="UpdateCommandResponse" 
    type="cp:CommandResponseType" 
    abstract="true"/>
  <xs:element 
    name="DeleteCommandResponse" 
    type="cp:CommandResponseType" 
    abstract="true"/>
</xs:schema>

Step 2. Define the WSDL interface for your Web service

Once the basic commands have been defined, we define our service interface using WSDL.

BlogInterface.wsdl
<?xml version="1.0" encoding="UTF-8"?>
<definitions 
  targetNamespace="CommandInterface"
  xmlns:tns="CommandInterface"
  xmlns:cp="CommandPattern"
  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
  xmlns="http://schemas.xmlsoap.org/wsdl/">
  
  <import namespace="CommandPattern" location="Command.xsd" />
          
  <message name="Post">
    <part name="command" element="cp:PostCommand" />
  </message>
  <message name="PostResponse">
    <part name="response" element="cp:PostCommandResponse" />
  </message>
  <message name="Update">
    <part name="command" element="cp:UpdateCommand" />
  </message>
  <message name="UpdateResponse">
    <part name="response" element="cp:UpdateCommandResponse" />
  </message>
  <message name="Delete">
    <part name="command" element="cp:DeleteCommand" />
  </message>
  <message name="DeleteResponse">
    <part name="response" element="cp:DeleteCommandResponse" />
  </message>
  
  <portType name="BlogInterface">
    <operation name="Post">
      <input message="tns:Post" />
      <output message="tns:PostResponse" />
    </operation>
    <operation name="Update">
      <input message="tns:Update" />
      <output message="tns:UpdateResponse" />
    </operation>
    <operation name="Delete">
      <input message="tns:Delete" />
      <output message="tns:DeleteResponse" />
    </operation>
  </portType>
  
  <binding name="BlogBinding" type="BlogInterface">
    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
    <operation name="Post">
      <soap:operation soapAction="PostCommand" />
      <input>
        <soap:body use="literal" />
      </input>
      <output>
        <soap:body use="literal" />
      </output>
    </operation>
    <operation name="Update">
      <soap:operation soapAction="UpdateCommand" />
      <input>
        <soap:body use="literal" />
      </input>
      <output>
        <soap:body use="literal" />
      </output>
    </operation>
    <operation name="Delete">
      <soap:operation soapAction="DeleteCommand" />
      <input>
        <soap:body use="literal" />
      </input>
      <output>
        <soap:body use="literal" />
      </output>
    </operation>
  </binding>
 
</definitions>

We now have an abstract interface definition that is capable of smooth, seamless evolution.

Step 3. Define the concrete commands for your Web service

Now let's flesh out our command structure a bit (we do need parameters after all)

BlogExample.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema 
  targetNamespace="Blogger" 
  xmlns:blog="Blogger" 
  xmlns:xs="http://www.w3.org/2001/XMLSchema" 
  xmlns:cp="CommandPattern" 
  elementFormDefault="qualified" 
  attributeFormDefault="unqualified">
  <xs:import 
    namespace="CommandPattern" 
    schemaLocation="Command.xsd"/>
  <xs:complexType name="PostItemType">
    <xs:complexContent>
      <xs:extension base="cp:PostCommandType">
        <xs:sequence>
          <xs:element ref="blog:Body"/>
        </xs:sequence>
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>
  <xs:complexType name="UpdateItemType">
    <xs:complexContent>
      <xs:extension base="cp:UpdateCommandType">
        <xs:sequence>
          <xs:element ref="blog:ID"/>
          <xs:element ref="blog:Body"/>
        </xs:sequence>
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>
  <xs:complexType name="DeleteItemType">
    <xs:complexContent>
      <xs:extension base="cp:DeleteCommandType">
        <xs:sequence>
          <xs:element ref="blog:ID"/>
        </xs:sequence>
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>
  <xs:complexType name="PostItemResponseType">
    <xs:complexContent>
      <xs:extension base="cp:CommandResponseType">
        <xs:sequence>
          <xs:element ref="blog:ID"/>
        </xs:sequence>
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>
  <xs:complexType name="UpdateItemResponseType">
    <xs:complexContent>
      <xs:extension base="cp:CommandResponseType"/>
    </xs:complexContent>
  </xs:complexType>
  <xs:complexType name="DeleteItemResponseType">
    <xs:complexContent>
      <xs:extension base="cp:CommandResponseType"/>
    </xs:complexContent>
  </xs:complexType>
  <xs:element 
    name="PostItem" 
    type="blog:PostItemType" 
    substitutionGroup="cp:PostCommand"/>
  <xs:element 
    name="UpdateItem" 
    type="blog:UpdateItemType" 
    substitutionGroup="cp:UpdateCommand"/>
  <xs:element 
    name="DeleteItem" 
    type="blog:DeleteItemType" 
    substitutionGroup="cp:DeleteCommand"/>
  <xs:element 
    name="PostItemResponse" 
    type="blog:PostItemResponseType" 
    substitutionGroup="cp:PostCommandResponse"/>
  <xs:element 
    name="UpdateItemResponse" 
    type="blog:UpdateItemResponseType" 
    substitutionGroup="cp:UpdateCommandResponse"/>
  <xs:element 
    name="DeleteItemResponse" 
    type="blog:DeleteItemResponseType" 
    substitutionGroup="cp:DeleteCommandResponse"/>
  <xs:element name="Body" type="xs:anyType"/>
  <xs:element name="ID" type="xs:string"/>
</xs:schema>

Wow, that was easily. Now look, see how the command parameters are decoupled from the WSDL interface description? We can add parameters all day long and never impact the WSDL description. Cool huh?

Step 4. Extend the concrete commands for your Web service (without changing the WSDL!)

To prove how wonderful this is, let's extend the Post and Update commands to add a Title parameter. We use XML Schema derivation to do this.

ExtendedBlogExample.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema 
  targetNamespace="ExtendedBlogger" 
  xmlns:cp="CommandPattern" 
  xmlns:blog="Blogger" 
  xmlns:xs="http://www.w3.org/2001/XMLSchema" 
  xmlns:eblog="ExtendedBlogger" 
  elementFormDefault="qualified" 
  attributeFormDefault="unqualified">
  <xs:import 
    namespace="Blogger" 
    schemaLocation="D:\Web\Inetpub\wwwroot\Schemas\Command\BlogExample.xsd"/>
  <xs:complexType name="PostItemType">
    <xs:complexContent>
      <xs:restriction base="blog:PostItemType">
        <xs:sequence>
          <xs:element ref="eblog:Title"/>
          <xs:element ref="blog:Body"/>
        </xs:sequence>
      </xs:restriction>
    </xs:complexContent>
  </xs:complexType>
  <xs:complexType name="UpdateItemType">
    <xs:complexContent>
      <xs:restriction base="blog:UpdateItemType">
        <xs:sequence>
          <xs:element ref="blog:ID"/>
          <xs:element ref="eblog:Title"/>
          <xs:element ref="blog:Body"/>
        </xs:sequence>
      </xs:restriction>
    </xs:complexContent>
  </xs:complexType>
  <xs:element 
    name="PostItem" 
    type="eblog:PostItemType" 
    substitutionGroup="cp:PostCommand"/>
  <xs:element 
    name="UpdateItem" 
    type="eblog:UpdateItemType" 
    substitutionGroup="cp:UpdateCommand"/>
  <xs:element name="Title" type="xs:string"/>
</xs:schema>

Using this mechanism, you can make as many changes to the commands as you'd like. You only have to change the WSDL when you add a new command to your service.


Copyright © 2002 James Snell.
Last update: 6/25/2002; 9:22:03 PM.