Saturday, May 14, 2011

Contract First and Code First Webservice Development

There are generally two styles for Webservice Development - Contract First and Code First.

Contract First is an approach where the developer starts from defining a contract for the webservice using a WSDL and then goes about generating/developing the codebase - typically using stacks that allow code to be generated from the wsdl.

Code First on the other hand is an approach where the developer starts by defining an interface, then generates the contract for the webservice using tools that allow translating an interface to wsdl.

I have used both approaches now for different projects over time and but am not very opinionated about either of the approaches. A purist approach would be to start with the contract, which I have personally advocated in the past, however in some real world projects I have tended to go with a code first approach, for a few reasons:

  1. Good tool support is required to define a reasonably involved wsdl - Eclipse is not good enough to create a quality wsdl - I would recommend IBM RAD or XML Spy for creating wsdl's
  2. A very deep knowledge of XML schema language is required to generate good constructs
  3. Development time is generally slower -  as creating a wsdl takes a good amount of time


Code First Approach on the other hand is quick. Let me illustrate by changing my DZone Example which is a Contract First example to a Code First using Apache CXF:

The contract for the service was:
<?xml version="1.0" encoding="UTF-8"?><wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:ms="http://bk.org/memberservice/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="memberservice" targetNamespace="http://bk.org/memberservice/">
 <wsdl:types>
  <xsd:schema targetNamespace="http://bk.org/memberservice/" elementFormDefault="qualified">
   <xsd:complexType name="MemberDetailType">
    <xsd:sequence>
     <xsd:element name="name" type="xsd:string"/>
     <xsd:element name="phone" type="xsd:string"/>
     <xsd:element name="city" type="xsd:string"/>
     <xsd:element name="state" type="xsd:string"/>
    </xsd:sequence>
   </xsd:complexType>
   <xsd:element name="MemberDetailsRequest">
    <xsd:complexType>
     <xsd:sequence>
      <xsd:element name="id" type="xsd:string"/>
     </xsd:sequence>
    </xsd:complexType>
   </xsd:element>
   <xsd:element name="MemberDetailsResponse">
    <xsd:complexType>
     <xsd:sequence>
      <xsd:element name="memberdetail" type="ms:MemberDetailType"/>
     </xsd:sequence>
    </xsd:complexType>
   </xsd:element>
  </xsd:schema>
 </wsdl:types>
 <wsdl:message name="MemberDetailsRequest">
  <wsdl:part element="ms:MemberDetailsRequest" name="parameters"/>
 </wsdl:message>
 <wsdl:message name="MemberDetailsResponse">
  <wsdl:part element="ms:MemberDetailsResponse" name="parameters"/>
 </wsdl:message>
 <wsdl:portType name="memberservice">
  <wsdl:operation name="GetMemberDetails">
   <wsdl:input message="ms:MemberDetailsRequest"/>
   <wsdl:output message="ms:MemberDetailsResponse"/>
  </wsdl:operation>
 </wsdl:portType>
 <wsdl:binding name="memberserviceSOAP" type="ms:memberservice">
  <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
  <wsdl:operation name="GetMemberDetails">
   <soap:operation soapAction="http://bk.org/memberservice/GetMemberDetails"/>
   <wsdl:input>
    <soap:body use="literal"/>
   </wsdl:input>
   <wsdl:output>
    <soap:body use="literal"/>
   </wsdl:output>
  </wsdl:operation>
 </wsdl:binding>
 <wsdl:service name="memberservice">
  <wsdl:port binding="ms:memberserviceSOAP" name="memberserviceSOAP">
   <soap:address location="http://localhost:8081/memberservice/services/MemberDetailsRequest"/>
  </wsdl:port>
 </wsdl:service>
</wsdl:definitions>

Let us start by defining a java interface that can sufficiently mimic this WSDL:
package org.bk.memberservice.endpoint;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;

import org.bk.memberservice.message.MemberDetailsRequest;
import org.bk.memberservice.message.MemberDetailsResponse;

@WebService
public interface MemberEndpoint {
 @WebMethod(operationName = "MemberDetailsRequest")
 @WebResult(name = "MemberDetailsResponse", targetNamespace = "http://bk.org/memberservice/")
 MemberDetailsResponse getMemberDetails(
   @WebParam(name = "MemberDetailsRequest") MemberDetailsRequest memberDetailsRequest);
}

and the JAXB2 annotations on the request and response types:

package org.bk.memberservice.message;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "MemberDetailsRequest", namespace="http://bk.org/memberservice/")
public class MemberDetailsRequest {

 public MemberDetailsRequest() {
 }

 public MemberDetailsRequest(String id) {
  this.id = id;
 }

 private String id;

 public String getId() {
  return id;
 }

 public void setId(String id) {
  this.id = id;
 }

}

package org.bk.memberservice.message;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

import org.bk.memberservice.types.MemberDetail;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "MemberDetailsResponse", namespace="http://bk.org/memberservice/")
public class MemberDetailsResponse {

 public MemberDetailsResponse() {
 }

 @XmlElement(name="memberdetail", namespace="http://bk.org/memberservice/")
 private MemberDetail memberDetail;

 public MemberDetailsResponse(MemberDetail memberDetail) {
  this.memberDetail = memberDetail;
 }

 public MemberDetail getMemberDetail() {
  return memberDetail;
 }

 public void setMemberDetail(MemberDetail memberDetail) {
  this.memberDetail = memberDetail;
 }

}
This generates a wsdl fairly close to the manually generated one, but not quite there - there does not seem to be a good way of controlling the namespace of operation name, and the wrappers around the request and the response xml structures.

So considering these factors, a rule that I have used is:

  • If a service is consumed only by a known internal client(say for cases where the same team is the producer AND the consumer) then go for code first, for the speed of development
  • If a service is expected to be more widely used, then go for contract first, with careful wsdl design

Sample code used here is available at :
git://github.com/bijukunjummen/memberservice-codefirst.git

No comments:

Post a Comment