SOA Initiative: Get your schemas and service contracts correct.

Published on : Jul 29, 2009

Category : BizTalk Server

Saravana

Author

SOA in an organisation is a journey; an organisation can’t change their existing strategy overnight. There are tons of articles written about SOA initiative, so I’m not going to add one more. In this article I’ll assume, your organisation has decided to implement SOA and as part of the journey you decided to implement your first service. Your journey is not going to stop after building your first service, so the organisation should plan and impose all of their SOA strategies and design principles when building their first service and treat it as a base platform for the forth coming services. When it comes to building services, the basic building blocks are schemas and service contracts. In technical terms it translates to xsd’s and wsdl’s. Often times refactoring schemas will have a ripple effect right up to the top level, so it worth putting enough time and effort in nailing down your schema (data) and service contract requirements. We’ll just assume this sample scenario: Your organisation is building an address service with a single operation called FindAddress. Using the service, consumers can search for addresses based on building number and post code (SearchRequest). The service in turn will return the list of addresses (SearchResponse) that matched the criteria. The scenario is simple, so we can concentrate on the core principles. The structure of our Address Service looks like this clip_image002 First of all, let’s break down the things required to construct our Address Services.
  1. We need a unique namespace to uniquely identify our Address service.
  2. We need unique identifier to access our operation FindAddress. This in web services world will be SOAPAction. Normally its good practice to club the service namespace with the operation name to generate this identifier.
  3. We need a request (SearchRequest) and a response (SearchResponse) message structure (types) required for our operation FindAddress.
There are various things you need to consider while designing the schemas and service contracts. Some of them will look so obvious and basic; things like folder structure, file names, namespace, placement of complex data structures, taking advantage of object oriented support for XSD’s etc. But they will prove very critical when the services start to evolve. The basic building block required for our address services is the request (SearchRequest) and response (SearchResponse) message structures. It was not so uncommon people dived directly into tools like Visual Studio, open up a .asmx/WCF web project or similar tools/techniques in other platforms and start building (implementing) the service before finalising the contract. The tools in turn auto generate the service contracts (wsdl). It’s a great help from the tooling point of view, but its not going to help us in long run. Maintainability and versioning will become a major issues, since the service contract and service implementation are tightly couples. A better approach will be to build the schemas and contracts in a modular way, so each module can evolve independently with various version numbers. I just picked up this quote (while reading a post from Martin Fowler) from Kent Beck, which I feel is very relevant, while designing the schemas: People can read your programs much more quickly and accurately if they can understand them in detail, then chunk those details into higher level structures.Kent Beck Basically Kent Beck is insisting on breaking large methods into well named smaller methods, a technique he refers to as Composed Method Patterns. That’s exactly what we are going to do here, we are going to break a large schema structure into smaller more maintainable structures in various files. The below figure explains what we are trying to achieve in a nutshell: clip_image004 Figure 1 When we translate the above picture into technical terms using xsd, wsld, import, data types etc. It will look like the one shown below. clip_image006 Figure 2 The hierarch and complexity of having multiple files to construct such simple message types might look overwhelming initially, but trust me it’s going to pay off in long run, when you start leveraging more services in your organisation. Target Namespaces for various levels
Filename Target Namespace
AddressService_1.wsdl http://yourorg.com/wsdl/addressService/1
AddressService_1.xsd http://yourorg.com/schemas/service/address/1
Address_1_0.xsd http://yourorg.com/schema/entity/address/1.0
OrgDataTypes_1_0.xsd http://yourorg.com/schema/entity/common/1.0
The numbers at the end of the namespaces and file names are there to mainly help us in versioning schemas and service contracts. I’ll explain them in my next article. For the time being please ignore them. Organisation wide common data models (OrgDataTypes_1_0.xsd): The very first thing you need to do before constructing any services in your organisation is to find all the common entities (data structures) that will be required across the organisation. It will be difficult to identify all of them at on go, but you can always start with the basic list and add more structures to it as and when you identify them. That’s the whole point of having those version numbers at the end and in the target namespace. Majority of the types in OrgDataTypes_1_0.xsd will have simple or complex type with restrictions appropriate for your organisation. Whenever you are constructing complex data models, don’t be tempted to use the standard xsd types directly inside your complex types. Example: While constructing the UKAddr ess type which requires a postcode, you could have easily end up using xsd:string type for post code. But in our solution we defined a simple type called PostCode which is an extension of xsd:string with a regular expression pattern restricting to only UK post code. In this way your schema validation will be effective and be able to capture the errors much earlier in the cycle. Also it will be a consistent pattern across the organisation. <xsd:simpleType name=”Postcode”> <xsd:restriction base=”xsd:string”> <xsd:pattern value=”((([A-Z]{2}[0-9]{1,2})|([A-Z]{1,2}[0-9][A-Z])|([A-Z][0-9]{1,2}))s([0-9][A-Z]{2})|(BFPOsd{1,4})|(GIRs0AA))”/> </xsd:restriction> </xsd:simpleType> Another important reason to build the common types used across the organisation is reusability; these types may be required across many services you build in the future. Example: The PostCode type may be required in the InventoryService to determine the stock location post code. It’s important you decide on the format of the namespace (target) you want to use. For our example I’ve chosen http://yourorg.com/schema/entity/common/1.0 Address Service specific data models (Address_1_0.xsd): After defining the common entity structures, the next step is to build the entity types required for our address service. Address_1_0.xsd file will contain only service specific simple and complex types required to construct the address service. The first thing you need to do is to import OrgDataTypes_1_0.xsd file into Address_1_0.xsd, which contains all the basic building blocks (entities) used common across the organisation. <xsd:import namespace=”http://yourorg.com/schema/entity/common/1.0″ schemaLocation=”../../Common/1.0/OrgDatatypes_1_0.xsd”/> Once the common entity schema is imported, the next important thing to do is to decide on a unique namespace (target) for the service specific schema Address_1_0.xsd. Make sure it’s different from the one we have used for the common entity schema. For our example I’ve chosen http://yourorg.com/schema/entity/address/1.0. Now you can start defining the entities required for the address service inside Address_1_0.xsd. Example: As shown in figure 2, Address_1_0.xsd contains the complex type UKAddress to represent a UK address structure, which is basically constructed using all the common entity types found in OrgDataTypes_1_0.xsd. Address Service specific messages (AddressService_1.xsd): This is the place where we construct the types that will be used directly by the service contracts WSDL. Similar to what we have done while constructing Address_1_0.xsd, the first thing we’ll do here is to import the required schemas (Address_1_0.xsd and if required you can download the OrgDataTypes_1_0.xsd as well if you are referring to types directly from common entities). In AddressService_1.xsd we will define types that are explicitly required to build the service contract (ex: SearchRequest, SearchResponse). Majority of the times these types won’t have any reusable value outside the context of the service. Types like service specific errors, warnings, etc are good candidate for this file. Again it’s essential to choose a consistent and unique target namespace format. For our example I’ve chosen http://yourorg.com/schemas/service/address/1. Constructing Service Interface (AddressService_1.wsdl) At this stage we got all the required building blocks to construct our Address Service WSDL. You can hand craft it, if you are familiar with the semantics of WSDL. Otherwise you can use the tools like XmlSpy, ThinkTectures WSCF (web service contract first) etc to construct the service WSDL. Folder Structure: Its also important to place the schemas and wsdl files in the right hierarch in the files system and contract repositories. You’ll referencing/importing schemas regularly in order to construct new service specific schemas and contracts (WSDL’s). Ex: <xsd:import namespace=”http://yourorg.com/schema/entity/common/1.0″ schemaLocation=”../../Common/1.0/OrgDatatypes_1_0.xsd”/> The below figure shows the simple structure  utilized for this sample scenario. image Advantages of taking this modular approach: There are various advantages in building the schemas and contracts in a modular way. Let see some of the key values 1. Reusability: The number one reason is the reusability of types. We have seen the organisation wide types (town, postcode etc) are being reused when constructing the UK Address type in address service. In the future some other service like example Inventory can reuse the UK Address type directly by importing the address schema (with appropriate version number). 2. Versioning: Individual modules can evolve independently with different version numbers. Providing greater degree of maintainability. 3. Extensibility: XSD supports object oriented approach while designing schemas, which can be utilized to extend the base types and create much more complex types if required.