В данной заметке мы продолжим тему разработки контрактов сервисов. В предыдущей статье мы отметили, что стандартом де-факто в настоящее время для представления контрактов сервисов является WSDL. Предполагается, что общение с сервисом осуществляется посредством сообщений, представленных в формате XML. Структура данных сообщений описывается с помощью языка XML Schema.
Существует несколько паттернов проектирования XML-схем. Использование данных паттернов позволяет описать одну и ту же структуру XML-документа разными способами, каждый из которых имеет свои преимущества и недостатки. Рассмотрим данные способы подробнее.
Суть данного паттерна заключается в том, что XML Schema является зеркалом описываемого ею XML-документа: если элемент A, содержит элемент B, а тот в свою очередь - элемент C, то и в XML Schema описания данных элементов будут вложены друг в друга.
Пример:
Достоинства паттерна: имеется только один корневой элемент, что обеспечивает максимально возможную инкапсуляцию.
Недостатки паттерна: невозможно повторное использование элементов (в приведенном примере видно, что элементы Address и LegalAddress имеют по-сути один и тот же тип, но их описание дублируется).
Суть данного паттерна заключается в том, что описываемый XML-документ разделяется на составные элементы, каждый из которых описывается в XML Schema как глобальный. Затем описанные элементы соединяются воедино. Данный паттерн находится на противоположном конце спектра по отношению к паттерну "Матрешка".
Пример:
Достоинства паттерна: схема содержит несколько глобальных элементов, что позволяет при необходимости легко разделить ее на отдельные файлы. Собственно, каждый элемент схемы является глобальным, что позволяет использовать данные элементы в других схемах.
Недостатки паттерна: не поддерживается инкапсуляция и сокрытие внутреннего представления элементов схемы.
Суть данного паттерна заключается в том, что описываемый XML-документ разделяется на составные типы, каждый из которых описывается в XML Schema как глобальный. Затем объявляется корневой элемент, соответствующий глобальному типу, соединяющему схему воедино.
Пример:
Достоинства паттерна: схема содержит только один корневой элемент, что обеспечивает хорошую инкапсуляцию. Каждый тип определяется как глобальный, что позволяет использовать данные типы в других схемах.
Недостатки паттерна: повсеместное применение пространств имен, что затрудняет использование схемы.
Понравилось сообщение - подпишитесь на блог и Twitter
Существует несколько паттернов проектирования XML-схем. Использование данных паттернов позволяет описать одну и ту же структуру XML-документа разными способами, каждый из которых имеет свои преимущества и недостатки. Рассмотрим данные способы подробнее.
Матрешка (Russian Doll)
Суть данного паттерна заключается в том, что XML Schema является зеркалом описываемого ею XML-документа: если элемент A, содержит элемент B, а тот в свою очередь - элемент C, то и в XML Schema описания данных элементов будут вложены друг в друга.
Пример:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://www.rt.ru/integration/messages/crm/abonent"
targetNamespace="http://www.rt.ru/integration/messages/crm/abonent"
elementFormDefault="qualified">
<xsd:element name="AbonentMessage">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Header">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="RecordId" type="xsd:decimal"/>
<xsd:element name="Operation">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="A"/>
<xsd:enumeration value="U"/>
<xsd:enumeration value="D"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
<xsd:element name="OperatorId" type="xsd:decimal"/>
<xsd:element name="TaskId" type="xsd:decimal"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="Body">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="AbonentType" minOccurs="1" maxOccurs="1">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="Organization"/>
<xsd:enumeration value="Person"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
<xsd:element name="Address">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="AddressId" type="xsd:integer"/>
<xsd:element name="Country" type="xsd:string"/>
<xsd:element name="Region" type="xsd:string"/>
<xsd:element name="District" type="xsd:string"/>
<xsd:element name="City" type="xsd:string"/>
<xsd:element name="Street" type="xsd:string"/>
<xsd:element name="House" type="xsd:string"/>
<xsd:element name="Flat" type="xsd:string"/>
<xsd:element name="PhoneNumber" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="LegalAddress">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="AddressId" type="xsd:integer"/>
<xsd:element name="Country" type="xsd:string"/>
<xsd:element name="Region" type="xsd:string"/>
<xsd:element name="District" type="xsd:string"/>
<xsd:element name="City" type="xsd:string"/>
<xsd:element name="Street" type="xsd:string"/>
<xsd:element name="House" type="xsd:string"/>
<xsd:element name="Flat" type="xsd:string"/>
<xsd:element name="PhoneNumber" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="INN" type="xsd:string"/>
<xsd:element name="Name" type="xsd:string"/>
<xsd:element name="FullName" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
xmlns:tns="http://www.rt.ru/integration/messages/crm/abonent"
targetNamespace="http://www.rt.ru/integration/messages/crm/abonent"
elementFormDefault="qualified">
<xsd:element name="AbonentMessage">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Header">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="RecordId" type="xsd:decimal"/>
<xsd:element name="Operation">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="A"/>
<xsd:enumeration value="U"/>
<xsd:enumeration value="D"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
<xsd:element name="OperatorId" type="xsd:decimal"/>
<xsd:element name="TaskId" type="xsd:decimal"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="Body">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="AbonentType" minOccurs="1" maxOccurs="1">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="Organization"/>
<xsd:enumeration value="Person"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
<xsd:element name="Address">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="AddressId" type="xsd:integer"/>
<xsd:element name="Country" type="xsd:string"/>
<xsd:element name="Region" type="xsd:string"/>
<xsd:element name="District" type="xsd:string"/>
<xsd:element name="City" type="xsd:string"/>
<xsd:element name="Street" type="xsd:string"/>
<xsd:element name="House" type="xsd:string"/>
<xsd:element name="Flat" type="xsd:string"/>
<xsd:element name="PhoneNumber" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="LegalAddress">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="AddressId" type="xsd:integer"/>
<xsd:element name="Country" type="xsd:string"/>
<xsd:element name="Region" type="xsd:string"/>
<xsd:element name="District" type="xsd:string"/>
<xsd:element name="City" type="xsd:string"/>
<xsd:element name="Street" type="xsd:string"/>
<xsd:element name="House" type="xsd:string"/>
<xsd:element name="Flat" type="xsd:string"/>
<xsd:element name="PhoneNumber" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="INN" type="xsd:string"/>
<xsd:element name="Name" type="xsd:string"/>
<xsd:element name="FullName" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
Достоинства паттерна: имеется только один корневой элемент, что обеспечивает максимально возможную инкапсуляцию.
Недостатки паттерна: невозможно повторное использование элементов (в приведенном примере видно, что элементы Address и LegalAddress имеют по-сути один и тот же тип, но их описание дублируется).
Ломтик салями (Salami Slice)
Суть данного паттерна заключается в том, что описываемый XML-документ разделяется на составные элементы, каждый из которых описывается в XML Schema как глобальный. Затем описанные элементы соединяются воедино. Данный паттерн находится на противоположном конце спектра по отношению к паттерну "Матрешка".
Пример:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://www.rt.ru/integration/messages/crm/abonent"
targetNamespace="http://www.rt.ru/integration/messages/crm/abonent"
elementFormDefault="qualified">
<xsd:element name="RecordId" type="xsd:decimal"/>
<xsd:element name="Operation">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="A"/>
<xsd:enumeration value="U"/>
<xsd:enumeration value="D"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
<xsd:element name="OperatorId" type="xsd:decimal"/>
<xsd:element name="TaskId" type="xsd:decimal"/>
<xsd:element name="Header">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="tns:RecordId"/>
<xsd:element ref="tns:Operation"/>
<xsd:element ref="OperatorId"/>
<xsd:element ref="TaskId"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="AbonentType">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="Organization"/>
<xsd:enumeration value="Person"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
<xsd:element name="AddressId" type="xsd:integer"/>
<xsd:element name="Country" type="xsd:string"/>
<xsd:element name="Region" type="xsd:string"/>
<xsd:element name="District" type="xsd:string"/>
<xsd:element name="City" type="xsd:string"/>
<xsd:element name="Street" type="xsd:string"/>
<xsd:element name="House" type="xsd:string"/>
<xsd:element name="Flat" type="xsd:string"/>
<xsd:element name="PhoneNumber" type="xsd:string"/>
<xsd:element name="Address">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="tns:AddressId"/>
<xsd:element ref="tns:Country"/>
<xsd:element ref="tns:Region"/>
<xsd:element ref="tns:District"/>
<xsd:element ref="tns:City"/>
<xsd:element ref="tns:Street"/>
<xsd:element ref="tns:House"/>
<xsd:element ref="tns:Flat"/>
<xsd:element ref="tns:PhoneNumber"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="LegalAddress">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="tns:AddressId"/>
<xsd:element ref="tns:Country"/>
<xsd:element ref="tns:Region"/>
<xsd:element ref="tns:District"/>
<xsd:element ref="tns:City"/>
<xsd:element ref="tns:Street"/>
<xsd:element ref="tns:House"/>
<xsd:element ref="tns:Flat"/>
<xsd:element ref="tns:PhoneNumber"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="INN" type="xsd:string"/>
<xsd:element name="Name" type="xsd:string"/>
<xsd:element name="FullName" type="xsd:string"/>
<xsd:element name="Body">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="tns:AbonentType" minOccurs="1" maxOccurs="1"/>
<xsd:element ref="tns:Address"/>
<xsd:element ref="tns:LegalAddress"/>
<xsd:element ref="tns:INN"/>
<xsd:element ref="tns:Name"/>
<xsd:element ref="tns:FullName"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="AbonentMessage">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="tns:Header"/>
<xsd:element ref="tns:Body"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
xmlns:tns="http://www.rt.ru/integration/messages/crm/abonent"
targetNamespace="http://www.rt.ru/integration/messages/crm/abonent"
elementFormDefault="qualified">
<xsd:element name="RecordId" type="xsd:decimal"/>
<xsd:element name="Operation">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="A"/>
<xsd:enumeration value="U"/>
<xsd:enumeration value="D"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
<xsd:element name="OperatorId" type="xsd:decimal"/>
<xsd:element name="TaskId" type="xsd:decimal"/>
<xsd:element name="Header">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="tns:RecordId"/>
<xsd:element ref="tns:Operation"/>
<xsd:element ref="OperatorId"/>
<xsd:element ref="TaskId"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="AbonentType">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="Organization"/>
<xsd:enumeration value="Person"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
<xsd:element name="AddressId" type="xsd:integer"/>
<xsd:element name="Country" type="xsd:string"/>
<xsd:element name="Region" type="xsd:string"/>
<xsd:element name="District" type="xsd:string"/>
<xsd:element name="City" type="xsd:string"/>
<xsd:element name="Street" type="xsd:string"/>
<xsd:element name="House" type="xsd:string"/>
<xsd:element name="Flat" type="xsd:string"/>
<xsd:element name="PhoneNumber" type="xsd:string"/>
<xsd:element name="Address">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="tns:AddressId"/>
<xsd:element ref="tns:Country"/>
<xsd:element ref="tns:Region"/>
<xsd:element ref="tns:District"/>
<xsd:element ref="tns:City"/>
<xsd:element ref="tns:Street"/>
<xsd:element ref="tns:House"/>
<xsd:element ref="tns:Flat"/>
<xsd:element ref="tns:PhoneNumber"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="LegalAddress">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="tns:AddressId"/>
<xsd:element ref="tns:Country"/>
<xsd:element ref="tns:Region"/>
<xsd:element ref="tns:District"/>
<xsd:element ref="tns:City"/>
<xsd:element ref="tns:Street"/>
<xsd:element ref="tns:House"/>
<xsd:element ref="tns:Flat"/>
<xsd:element ref="tns:PhoneNumber"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="INN" type="xsd:string"/>
<xsd:element name="Name" type="xsd:string"/>
<xsd:element name="FullName" type="xsd:string"/>
<xsd:element name="Body">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="tns:AbonentType" minOccurs="1" maxOccurs="1"/>
<xsd:element ref="tns:Address"/>
<xsd:element ref="tns:LegalAddress"/>
<xsd:element ref="tns:INN"/>
<xsd:element ref="tns:Name"/>
<xsd:element ref="tns:FullName"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="AbonentMessage">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="tns:Header"/>
<xsd:element ref="tns:Body"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
Достоинства паттерна: схема содержит несколько глобальных элементов, что позволяет при необходимости легко разделить ее на отдельные файлы. Собственно, каждый элемент схемы является глобальным, что позволяет использовать данные элементы в других схемах.
Недостатки паттерна: не поддерживается инкапсуляция и сокрытие внутреннего представления элементов схемы.
Подъемные жалюзи (Venetian Blind)
Суть данного паттерна заключается в том, что описываемый XML-документ разделяется на составные типы, каждый из которых описывается в XML Schema как глобальный. Затем объявляется корневой элемент, соответствующий глобальному типу, соединяющему схему воедино.
Пример:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://www.rt.ru/integration/messages/crm/abonent"
targetNamespace="http://www.rt.ru/integration/messages/crm/abonent"
elementFormDefault="qualified">
<xsd:simpleType name="tOperation">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="A"/>
<xsd:enumeration value="U"/>
<xsd:enumeration value="D"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:complexType name="tHeader">
<xsd:sequence>
<xsd:element name="RecordId" type="xsd:decimal"/>
<xsd:element name="Operation" type="tns:tOperation"/>
<xsd:element name="OperatorId" type="xsd:decimal"/>
<xsd:element name="TaskId" type="xsd:decimal"/>
</xsd:sequence>
</xsd:complexType>
<xsd:simpleType name="tAbonentType">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="Organization"/>
<xsd:enumeration value="Person"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:complexType name="tAddress">
<xsd:sequence>
<xsd:element name="AddressId" type="xsd:integer"/>
<xsd:element name="Country" type="xsd:string"/>
<xsd:element name="Region" type="xsd:string"/>
<xsd:element name="District" type="xsd:string"/>
<xsd:element name="City" type="xsd:string"/>
<xsd:element name="Street" type="xsd:string"/>
<xsd:element name="House" type="xsd:string"/>
<xsd:element name="Flat" type="xsd:string"/>
<xsd:element name="PhoneNumber" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="tBody">
<xsd:sequence>
<xsd:element name="AbonentType" type="tns:tAbonentType" minOccurs="1" maxOccurs="1"/>
<xsd:element name="Address" type="tns:tAddress"/>
<xsd:element name="LegalAddress" type="tns:tAddress"/>
<xsd:element name="INN" type="xsd:string"/>
<xsd:element name="Name" type="xsd:string"/>
<xsd:element name="FullName" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="tAbonentMessage">
<xsd:sequence>
<xsd:element name="Header" type="tns:tHeader"/>
<xsd:element name="Body" type="tns:tBody"/>
</xsd:sequence>
</xsd:complexType>
<xsd:element name="AbonentMessage" type="tns:tAbonentMessage"/>
</xsd:schema>
xmlns:tns="http://www.rt.ru/integration/messages/crm/abonent"
targetNamespace="http://www.rt.ru/integration/messages/crm/abonent"
elementFormDefault="qualified">
<xsd:simpleType name="tOperation">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="A"/>
<xsd:enumeration value="U"/>
<xsd:enumeration value="D"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:complexType name="tHeader">
<xsd:sequence>
<xsd:element name="RecordId" type="xsd:decimal"/>
<xsd:element name="Operation" type="tns:tOperation"/>
<xsd:element name="OperatorId" type="xsd:decimal"/>
<xsd:element name="TaskId" type="xsd:decimal"/>
</xsd:sequence>
</xsd:complexType>
<xsd:simpleType name="tAbonentType">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="Organization"/>
<xsd:enumeration value="Person"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:complexType name="tAddress">
<xsd:sequence>
<xsd:element name="AddressId" type="xsd:integer"/>
<xsd:element name="Country" type="xsd:string"/>
<xsd:element name="Region" type="xsd:string"/>
<xsd:element name="District" type="xsd:string"/>
<xsd:element name="City" type="xsd:string"/>
<xsd:element name="Street" type="xsd:string"/>
<xsd:element name="House" type="xsd:string"/>
<xsd:element name="Flat" type="xsd:string"/>
<xsd:element name="PhoneNumber" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="tBody">
<xsd:sequence>
<xsd:element name="AbonentType" type="tns:tAbonentType" minOccurs="1" maxOccurs="1"/>
<xsd:element name="Address" type="tns:tAddress"/>
<xsd:element name="LegalAddress" type="tns:tAddress"/>
<xsd:element name="INN" type="xsd:string"/>
<xsd:element name="Name" type="xsd:string"/>
<xsd:element name="FullName" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="tAbonentMessage">
<xsd:sequence>
<xsd:element name="Header" type="tns:tHeader"/>
<xsd:element name="Body" type="tns:tBody"/>
</xsd:sequence>
</xsd:complexType>
<xsd:element name="AbonentMessage" type="tns:tAbonentMessage"/>
</xsd:schema>
Достоинства паттерна: схема содержит только один корневой элемент, что обеспечивает хорошую инкапсуляцию. Каждый тип определяется как глобальный, что позволяет использовать данные типы в других схемах.
Недостатки паттерна: повсеместное применение пространств имен, что затрудняет использование схемы.
Какой же паттерн использовать
- Если требуется гибкость при управлении пространствами имен, а так же необходимо повторное использование компонентов схемы, то следует использовать паттерн Подъемные жалюзи.
- Если необходимо обеспечить авторам XML-документов возможность подстановки элементов, то следует использовать паттерн Ломтик салями.
- Если необходимо минимизировать размер схемы и связанность (coupling) между ее компонентами, то следует использовать паттерн Матрешка.
Ресурсы
- XML Schema Design Pattern - Handling Versioning and Reuse.
- Global versus Local (A Collectively Developed Set of Schema Design Guidelines).
Понравилось сообщение - подпишитесь на блог и Twitter
2 комментария:
вот никогда не понимал "фишку" в этих бесконечных <xsd: в файле схемы, почему не начать файл с "<schema xmlns="http://www.w3.org/2001/XMLSchema" ... " и таким образом сделать пустым префикс для тегов относящихся к xml schema?
И "xmlns:tns="http://www.rt.ru/integration/messages/crm/abonent"" tns - подразумевается targetNamespace? тогда это тоже не удачный выбор, по-моему было бы лучше и в какой-то степени умнее с "xmlns:abonent="http://www.rt.ru/integration/messages/crm/abonent""
Как-то странно, xsd вас почему-то напрягает, а префикс из 7-ми букв вместо префикса из 3-х букв почему-то нет. Непоследовательность какая-то.
Все ваши рассуждения можно считать актуальными, если в файле схемы используется только одно пространство имен. Если же в схему импортируется еще 4 - 5 схем, каждая из которых имеет свое пространство имен (а при проектировании сколь нибудь сложной канонической модели данных это - не редкость), то префиксы xsd и tns облегчают чтение, сразу становится понятно, что вот это - xsd - из пространства имен самой XML Schema, а вот это - tns - из той схемы, которую мы описываем, а всякие abn, cust, org и т.д. - мы импортировали.
Отправить комментарий
Любой Ваш комментарий важен для меня, однако, помните, что действует предмодерация. Давайте уважать друг друга!