• 沒有找到結果。

Designing Schema for SOA

在文檔中 Java SOA Cookbook (頁 67-72)

XML Schema and the SOA Data Model

2.1 Designing Schema for SOA

Problem

You want to define your data types with XML Schema to use within your SOA, but Schema is so flexible that you aren’t sure how to do it. You want to follow patterns and best practices for Schema that make the most sense specifically in an SOA context.

Solution

Follow one of the Schema design patterns discussed here: Russian Doll, Salami Slice, or Venetian Blind.

There are several generally accepted design patterns that apply when creating XML schemas. The inherent flexibility that XML Schema affords means that it can be difficult to figure out how to start writing them in a consistent, clear manner that will give you the perfect combination of expressiveness, flexibility, and strong-enough typing. This is all aggravated when your aim is to design them for an SOA in a way that allows you both to generate to Java code and to use as raw XML.

Schema defines basic building blocks for defining entities: simple types, complex types, elements, and attributes. Beyond these, there are many choices to make regarding global or local types, namespace qualification, and more. Making uninformed choices at this level can crush your SOA, inadvertently limiting its flexibility. Without careful schema design, you could work very hard to make loosely coupled services that are composed using orchestrations and brokered ESBs for different domains, only to sud-denly find that in reality the services in your SOA are very tightly coupled at the root because of a poor choice in schema design. A simple schema change here could force you to redeploy whole sets of service compositions.

But XML is at the heart of your SOA, and you want to use the considerable power of Java while maintaining the flexibility that XML gives you. You can have it both ways, but you just need to think about the ramifications of your schema design choices. In this section, we’ll look at three well-known design patterns for constructing schemas:

Russian Doll, Salami Slice, and Venetian Blind. There are two others that people some-times employ: Garden of Eden and Chameleon, which we’ll have to discuss in the following recipe because of the attractive nuisance it makes.

The patterns here are generally differentiated by one thing: whether or not your ele-ments and types are globally defined. A global element or type is one that is a child of the schema node. A local element or type is one that is nested within another element or type. A local element cannot be reused elsewhere.

Now let’s look at the patterns.

Russian Doll

In real life, a Russian doll is a wooden shell that acts as a container for several other identical-looking dolls, each of which is also a shell containing the next smaller doll.

Opening the top of the outermost doll reveals the next smaller doll.

This design pattern is perfectly named, and therefore easy to remember. The pattern is to create a single global root element that contains, like its namesake, all of its constit-uent types; all other elements beside this single global element are local. All element declarations are nested inside the root element, and can therefore only be used once, within that context. The root element is the only one that gets to use the global namespace.

Russian Doll has the following characteristics:

• It has a single global root element.

• All types are local, i.e., nested within the root element.

• It supports only schemas designed entirely in a single file.

• It has high cohesion with minimal coupling.

• Because types are not exposed, the schema is fully encapsulated.

• It is the easiest pattern to read and write.

Do not use Russian Doll if you need to reuse types.

Example 2-1 demonstrates the Russian Doll design pattern.

Example 2-1. Book.xsd using the Russian Doll schema design pattern

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"

targetNamespace="http://ns.soacookbook.com/russiandoll"

xmlns:tns="http://ns.soacookbook.com/russiandoll"

elementFormDefault="unqualified">

<xsd:annotation>

<xsd:documentation>

Book schema as Russian Doll design.

</xsd:documentation>

</xsd:annotation>

<xsd:element name="book">

<xsd:complexType>

<xsd:sequence>

<xsd:element name="title" type="xsd:string"/>

<xsd:element name="price" type="xsd:decimal"/>

<xsd:element name="category" type="xsd:NCName"/>

<xsd:choice>

<xsd:element name="author" type="xsd:string"/>

<xsd:element name="authors">

<xsd:complexType>

<xsd:sequence>

<xsd:element name="author"

type="xsd:string"

maxOccurs="unbounded"/>

</xsd:sequence>

</xsd:complexType>

</xsd:element>

</xsd:choice>

</xsd:sequence>

</xsd:complexType>

</xsd:element>

</xsd:schema>

As you can see, this schema defines a single global element, “book”. The types required to create a book element are all nested within it. Elements and types cannot be refer-enced. Namespaces are localized. The “authors” element redefines its child “author”

elements. This is potentially a maintenance issue.

Russian Doll has certain advantages. For simple schemas, it is easy to read and write because there is no mixing, no references, no hunting for type definitions. There is no flexibility built into this design, which makes it predictable. It is easy to understand the intentions of the author, and you know exactly what the resulting document instance will look like.

Because it is entirely self-contained, schemas that follow this pattern are decoupled from other schemas. Changing types will not affect other schemas.

The clear disadvantage to Russian Doll with respect to straight XML work is that the types you carefully define cannot be reused elsewhere. And it can become unwieldy for very large schemas.

Russian Doll is perhaps an appropriate design to use when wrapping legacy data from a source that is fairly static, say, a modified DB2 filesystem table on a midrange that holds isolated records. Within a master data management effort, this sort of definition can be reflected quickly by generative tools.

Salami Slice

Salami Slice represents the opposite end of the design spectrum from Russian Doll.

Using this pattern, you declare all elements as global, but declare all types locally. You set all elements into the global namespace, making the schema available for reuse by other schemas. Each element acts as a single definition “slice,” which can be combined with others.

With Salami Slice, you have many components, all defined individually, which are then brought together under global elements. Russian Doll is absolutely rigid and inflexible in its design, giving you the closed definition of the single element it defines. Salami Slice is entirely open, allowing a wide variety of possible combinations. The physical structure of an actual Russian doll allows only one way to put the doll together; slices of salami offer no guidance for how they might be arranged on a sandwich.

Salami Slice has the following characteristics:

• All elements are global.

• All elements are defined within the global namespace.

• All types are local.

• Element declarations are never nested.

• Element declarations are reusable. Salami Slice offers the best possibility for reuse of all the schema design patterns.

• It is difficult to determine the intended root element, as there are many potential choices.

Example 2-2 shows your book schema redesigned with Salami Slice.

Example 2-2. Book.xsd using the Salami Slice schema design pattern

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"

targetNamespace="http://ns.soacookbook.com/salami"

xmlns:tns="http://ns.soacookbook.com/salami"

elementFormDefault="qualified">

<xsd:annotation>

<xsd:documentation>

Book schema as Salami Slice design.

</xsd:documentation>

</xsd:annotation>

<xsd:element name="book">

<xsd:complexType>

<xsd:sequence>

<xsd:element ref="tns:title" />

<xsd:element ref="tns:author" />

<xsd:element ref="tns:category" />

<xsd:element ref="tns:price" />

</xsd:sequence>

</xsd:complexType>

</xsd:element>

<xsd:element name="title"/>

<xsd:element name="price"/>

<xsd:element name="category"/>

<xsd:element name="author"/>

</xsd:schema>

The advantage to this pattern is that because the elements are declared globally, the schema is reusable. But because changing an element affects the composing elements, Salami Slice schemas are considered tightly coupled.

Schemas that follow this pattern are verbose. Things are clearly arranged and flat.

Note too that schema reuse can often mean tight coupling for your services. See the discussion at the end of this recipe for more on that matter.

The Russian Doll and the Salami Slice operate at opposite ends of the spectrum. Because of their purity in insisting on either total rigidity or total flexibility, they have clear advantages and clear disadvantages. The next pattern, Venetian Blind, meets in the middle and offers the best of both worlds.

Venetian Blind

Venetian Blind is an extension of Russian Doll. It contains only one single global root element. It departs from Russian Doll in that it allows for reuse of all types as well as the global root element.

Using Venetian Blind means that you define a single global root element for instantia-tion, and compose it with externally defined types. This has the benefit of maximizing reuse.

Venetian Blind has the following characteristics:

• It has a single global root element.

• It mixes global and local declarations. Contrast this with Russian Doll, in which all types are local, and Salami Slice, in which all types are global.

• It has high cohesion but also high coupling. Because its components are coupled and not self-contained, it can occasion coupling with other schemas.

• It maximizes reuse. All types and the root element can be recombined.

• Because types are exposed, encapsulation is limited.

• It allows you to use multiple files to define your schema.

• It is verbose. Breaking apart each type in this way gives you very selective, granular control over each individual aspect or your element, but makes for a lot of typing.

In Example 2-3, there are five reusable types: TitleType, AuthorType, CategoryType, PriceType, and the book element itself.

Example 2-3. Book schema using the Venetian Blind design pattern

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"

targetNamespace="http://ns.soacookbook.com/venetianblind"

xmlns:tns="http://ns.soacookbook.com/venetianblind"

elementFormDefault="unqualified"

attributeFormDefault="unqualified">

<xsd:annotation>

<xsd:documentation>

Book schema as Venetian Blind design.

</xsd:documentation>

</xsd:annotation>

<!-- Single global root element exposed -->

<xsd:element name="book" type="tns:BookType" />

<!-- The root is given a type that is defined here, using all externally defined elements.-->

<xsd:complexType name="BookType">

<xsd:sequence>

<xsd:element name="title" type="tns:TitleType"/>

<xsd:element name="author" type="tns:AuthorType"/>

<xsd:element name="category" type="tns:CategoryType"/>

<xsd:element name="price" type="tns:PriceType" />

</xsd:sequence>

</xsd:complexType>

<!-- Each type used by the global root is defined below, and are potentially available for reuse depending on the value of the 'elementFormDefault' switch

(use 'qualified' to expose, 'unqualified' to hide) -->

<xsd:simpleType name="TitleType">

<xsd:restriction base="xsd:string">

<xsd:minLength value="1"/>

</xsd:restriction>

</xsd:simpleType>

<xsd:simpleType name="AuthorType">

<xsd:restriction base="xsd:string">

<xsd:minLength value="1"/>

</xsd:restriction>

</xsd:simpleType>

<xsd:simpleType name="CategoryType">

<xsd:restriction base="xsd:string">

<xsd:enumeration value="LITERATURE"/>

<xsd:enumeration value="PHILOSOPHY"/>

<xsd:enumeration value="PROGRAMMING"/>

</xsd:restriction>

</xsd:simpleType>

<xsd:simpleType name="PriceType">

<xsd:restriction base="xsd:float" />

</xsd:simpleType>

</xsd:schema>

Choose Venetian Blind when you need to maximize reuse and flexibility, and take advantage of namespace exposure.

See Also

Recipe 2.2.

在文檔中 Java SOA Cookbook (頁 67-72)