December 14, 2009

XML Schema Design Pattern - Handling Versioning and Reuse

In my last project I have been working with service orientated integration and ESB, in such an architecture the domain model is represented with a canonical model of a XML schema. There several way you can design your schema which I will describe bellow, but what is more imported is the implication it has on:
  • Level of possibility to reuse your canonical model in different service.
  • Level of possibility to handle different version of same service.
  • Level of possibility to split your canonical data model into bit of pieces and process them in parallel.
Before laying out the different patterns I will explain different definition that I will use when evaluating the different patterns

Definition:
Root element – the first element a XML must contain. If a schema contains several root element it is possible to slice the XML document into several new XML document.

Global element – the elements that directly comes after the root element, possibly elements are 'element', 'complexType' and 'simpleType'.

Local element – nested element inside global element.

Russian Doll Example

Pros:
  • One root element, good for encapsulation.
  • All elements are local and encapsulated.
Cons:
  • No reuse of elements.
  • Easier to get started with.

Example Book-RussianDoll-v1.0.0.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
     targetNamespace="http://ns.msc.se/examples/book-russiandoll/v1_0_0"
     xmlns:russ="http://ns.msc.se/examples/book-russiandoll/v1_0_0" 
     elementFormDefault="qualified">
 
 <xs:element name="book">
  <xs:complexType>
   <xs:sequence>
    <xs:element name="Title" type="xs:string" />
    <xs:choice>
     <xs:element name="AuthorList" minOccurs="0" maxOccurs="unbounded">
      <xs:complexType>
       <xs:sequence>
        <xs:element name="Author">
         <xs:complexType>
          <xs:sequence>
           <xs:element name="givenName" type="xs:string" />
          </xs:sequence>
         </xs:complexType> 
        </xs:element>
       </xs:sequence>
      </xs:complexType>
     </xs:element>
    </xs:choice>
   </xs:sequence>
  </xs:complexType>
 </xs:element> 
 
</xs:schema>

Salami Slide
Pros
  • Many root elements, the XML file can be sliced into numerous ways.
  • All elements are global, which allows reuse in other Schemas.

Cons
  • Does not encapsulate and hide the schema.
  • Usage of namespaces are almost a most, which makes usage more complex.

Example Book-SalamiSlice-v1.0.0.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
     targetNamespace="http://ns.msc.se/examples/book-salamislice/v1_0_0"
     xmlns:salv1_0_0="http://ns.msc.se/examples/book-salamislice/v1_0_0" 
     elementFormDefault="qualified">

 <xs:element name="book">
  <xs:complexType>
   <xs:sequence>
    <xs:element ref="salv1_0_0:Title" />
    <xs:element ref="salv1_0_0:AuthorList" />
   </xs:sequence>
  </xs:complexType>
 </xs:element>

 <xs:element name="Title" type="xs:string" />

 <xs:element name="AuthorList">
  <xs:complexType>
   <xs:sequence>
    <xs:element ref="salv1_0_0:Author" minOccurs="0" maxOccurs="unbounded" />
   </xs:sequence>
  </xs:complexType>
 </xs:element>

 <xs:element name="Author">
  <xs:complexType>
   <xs:sequence>
    <xs:element ref="salv1_0_0:GivenName" />
    <xs:element ref="salv1_0_0:Surname" />
   </xs:sequence>
  </xs:complexType>
 </xs:element>
 
 <xs:element name="GivenName" type="xs:string" />
 
 <xs:element name="Surname" type="xs:string" />
 
</xs:schema>

When generating code via JAXB, wee can see that several classes are annotated with the @XmlRootElement. This says that we have several possible root element.

In the next example we show how to handle version and extending previous schema version.

Example Book-SalamiSlice-v1.0.1.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
     targetNamespace="http://ns.msc.se/examples/book-salamislice/v1_0_1"
     xmlns:salv1_0_0="http://ns.msc.se/examples/book-salamislice/v1_0_0"
     xmlns:salv1_0_1="http://ns.msc.se/examples/book-salamislice/v1_0_1" 
     elementFormDefault="qualified">
 
 <xs:import namespace="http://ns.msc.se/examples/book-salamislice/v1_0_0" schemaLocation="Book-SalamiSlice-v1.0.0.xsd" />

 <xs:element name="book">
  <xs:complexType>
   <xs:sequence>
    <xs:element ref="salv1_0_1:Title" />
    <xs:element ref="salv1_0_1:Category" />
    <xs:element ref="salv1_0_0:AuthorList" />
   </xs:sequence>
  </xs:complexType>
 </xs:element>
 
 <xs:element name="Title">
  <xs:simpleType>
   <xs:restriction base="xs:string">
    <xs:minLength value="3" />
   </xs:restriction>
  </xs:simpleType>
 </xs:element>
 
 <xs:element name="Category" type="xs:string" />
 
</xs:schema>

Venetian Blind

Pros:
  • Only one root element, good for encapsulation.
  • All element are global, which allows reuse in other Schemas.
Cons:
  • Usage of namespaces are almost a most, which makes usage more complex.

Example Book-VenetianBlind-v1.0.0.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
     targetNamespace="http://ns.msc.se/examples/book-venetianblind/v1_0_0"
     xmlns:ven="http://ns.msc.se/examples/book-venetianblind/v1_0_0" 
     elementFormDefault="qualified">

 <xs:element name="Book">
  <xs:complexType>
   <xs:sequence>
    <xs:element name="Title" type="ven:TitleType" />
    <xs:element name="AuthorList" type="ven:AuthorListType" />
   </xs:sequence>
  </xs:complexType>
 </xs:element>
 
 <xs:simpleType name="TitleType">
  <xs:restriction base="xs:string">
  </xs:restriction>
 </xs:simpleType>

 <xs:complexType name="AuthorListType">
  <xs:sequence>
   <xs:element name="Author" type="ven:AuthorType" minOccurs="0" maxOccurs="unbounded" />
  </xs:sequence>
 </xs:complexType>
 
 <xs:complexType name="AuthorType">
  <xs:sequence>
   <xs:element name="GivenName" type="ven:GivenNameType" />
   <xs:element name="Surname" type="ven:SurnameType" />
  </xs:sequence>
 </xs:complexType>
 
 <xs:simpleType name="GivenNameType">
  <xs:restriction base="xs:string">
  </xs:restriction>
 </xs:simpleType>

 <xs:simpleType name="SurnameType">
  <xs:restriction base="xs:string">
  </xs:restriction>
 </xs:simpleType>
 
</xs:schema>

Example Book-VenetianBlind-v1.0.1.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
     targetNamespace="http://ns.msc.se/examples/book-venetianblind/v1_0_1"
     xmlns:ven1_0_0="http://ns.msc.se/examples/book-venetianblind/v1_0_0"
     xmlns:ven1_0_1="http://ns.msc.se/examples/book-venetianblind/v1_0_1" 
     elementFormDefault="qualified">

 <xs:import namespace="http://ns.msc.se/examples/book-venetianblind/v1_0_0" schemaLocation="Book-VenetianBlind-v1.0.0.xsd" />

 <xs:element name="Book">
  <xs:complexType>
   <xs:sequence>
    <xs:element name="Title" type="ven1_0_1:TitleType" nillable="true" />
    <xs:element name="Category" type="ven1_0_1:CategoryType" nillable="true" />
    <xs:element name="AuthorList" type="ven1_0_0:AuthorListType" />
   </xs:sequence>
  </xs:complexType>
 </xs:element>
 
 <xs:simpleType name="TitleType">
  <xs:restriction base="xs:string">
  </xs:restriction>
 </xs:simpleType>

 <xs:simpleType name="CategoryType">
  <xs:restriction base="xs:string">
   <xs:enumeration value="SPORT" />
   <xs:enumeration value="COOKING" />
   <xs:enumeration value="PROGRAMMING" />
  </xs:restriction>
 </xs:simpleType>
 
</xs:schema>

Conclusion
When starting of thinking of Schema one come to the conclusion that schema design is much like tradition OO design, where one must always consider if a properties and method should be hidden or exposed to other classes. The same yields for Schemas and in the real world one would scarcely use just one design pattern, but rather combine them.

No comments: