September 2007 - Posts

SOA tips

 

David Pallman from Neudesic has been blogging a set of SOA tips that most developers should consider as part of their SOA projects (via Sam Gentile). Although the posts are titled WCF tips some of the principles are relevant to any Web Services/SOA implementation. I've decided to provide a first iteration with some complements to David's thoughts.

Consider versioning

Tip: When designing service contracts keep in mind that contracts are not static entities and are subject to change.

Rationale: Service contracts are dynamic entities that evolve during over time. Designing for versioning facilitates the evolution of the services without breaking the existing clients

What happens if I don't: Most likely you will find yourself in a situation in which you have to affect some of your consumers in order to deploy new versions of the services.

Leverage consumer-driven contracts (related with the above)

Tip: Distributed different versions of contracts to specific client based on the functionality they are looking to leverage.

Rationale: The classic approach to service orientation states that services implement a one or multiple static contracts that are used by all the consumers. However, this approach assumes that all consumers will use the same interface for interacting with the service weather in reality they might only be interested in using a set of specific features. Distributing specific contracts to consumers facilitates important aspects of SOA such as versioning. This approach is known as consumer driven contracts.

What happen if I don't: All the consumers will have the same interface to communicate with the services and consequently the versioning strategy becomes even more important.

Design interoperable contracts

Tip: Interoperability is a key component of any successful SOA history. When designing service contracts, make sure that the contracts can be consumed using heterogeneous technologies.

Rationale: At this point we all know that XML and objects don't match very well. The first step of achieving interoperability is making sure that service, data and message contracts can be interpreted by the consumer technology.

What happens if I don't: You are limiting the functionality of the service to the consumers that can interoperate with it.

Design code first- contract aware

Tip: Apply a code first approach to maximize the productivity of your development team and a contract first approach to maximize interoperability.

Rationale: The debate between contract-first and code-first has been around for years. The causes of the problem resides in, surprise surprise, the fact that XML and object don't match very well. I think the correct answer for real world applications is a combination of both approaches. Only a few people know WSDL well enough to design contract-first services that also have an optimal representation in the object oriented language in which the service is implemented. Similarly, if you only design code-first services most likely the result WSDL might be suboptimal. In my last article for SOA World you can find more details about this technique.

What happens if I don't: A poor contract design will impact everything in your SOA application.

Don't use transaction across multiple services.

Tip: Transactions across multiple services is one of the aspects that can break the scalability of SOA applications. Services can perform transactional operations but expanding the transaction across multiple services is not a recommended practice for most real world applications.

Rationale: There are several WS-* protocols that can be used to implement either atomic or long running transactions. After a few years debating, the vendors seems to agree that the combination of WS-Coordination, WS-AtomicTransaction and WS-BusinessActivity is the way to tackle this problem. However, only WCF and Sun WSIT provide solid implementations of WS-AT although there are other emerging implementations such as Axis2. This means that right now is unlikely to achieve high levels of interoperability when using any of those protocols. Most  important, distributed transactions across services are always resource-expensive operations that limit the loosely coupling and scalability of SOA applications

What happens if I don't: Good luck with that.

 Leverage WS-* protocols on the right way

Tip: Make sure to apply the correct WS-* protocol to the specific scenario you are trying to implement.

Rationale: Nowadays the vendors have produced more than one hundred WS-* specifications, several versions of the same protocol and around a dozen of implementations. Leveraging the right WS-* protocol for a specific scenario is a combination of both understanding the protocol as well as the specific implementation you are using. The use of WS-* protocols directly impact important SOA aspects such as versioning and interoperability, governance, etc.

What happens if I don't: Applying the incorrect WS-* protocols can affect the complete behavior of a SOA solution not to mention cost to maintenance and versioning.

I will follow up this post with more tips specific related to the use of WS-* protocols.

 

IBM on ESB

Check out these two interesting papers regarding IBM’s vision ESB’s role in Service Orientation.

 ·         Why the ESB is a fundamental part of SOA ·         ESB-oriented architecture: The wrong approach to adopting SOA

Microsoft's MDM strategy

Check out this new website that contains some references to Microsoft’s MDM strategy including the Product Roadmap.
Posted by gsusx | 1 comment(s)
Filed under: ,

Defining service contracts programmatically on .NET Framework 3.5

As I referred on a previous post, one of the most attractive features in the upcoming .NET Framework 3.5 (Orcas) is the combination of Workflow Services and Durable Services to implement long running services combining Windows Communication Foundation (WCF) and Windows Workflow Foundation (WF). The main vision behind Workflow Services is to integrate the long running capabilities of WF with the messaging and contract authoring capabilities of WCF. Specifically for contract authoring, Workflow Services provide two main authoring styles:

  • Contract-First Workflow Services: A contract-first workflow service is a workflow that uses preexisting service contract information. The contract information can be either downloaded from a url or defined within the workflow project.
  • Workflow-First Workflow Services: The alternative to the contract first approach is to create the contract together with the workflow. The typical way of creating workflow-first workflow services is using the editors tools included in the Receive activity.

Workflow-First Services can be defined at design time; my friend Matt Winkle has posted a few samples about this technique. Complementary, Workflow-First Workflow Services can also be generated programmatically.

Generating contracts dynamically is mostly applied on scenarios on which contracts are based on metadata that is constantly changing. A classic example is contracts that reflect the type information of Line of business (LOB) systems such as SAP or Siebel.  What makes those scenarios different from the classic contract design, is the fact that the WSDL needs to reflect type information that is constantly changing which makes the traditional approach to contract creation unpractical in most of the cases. Including strongly typed information as part of the WSDL facilitates the consumption of the services from applications that depend heavily on type information at design time such as BizTalk Server or SQL Server Integration Services. The Salesforce.com Apex API is a great example of dynamic contract generation; in this case for a CRM system. If you want to get more familiar with technologies that leverage this approach of generating dynamic Service contracts take a look at the recently released WCF LOB Adapters SDK.

In the current WF version included as part of Orcas we can create WCF contracts programmatically using the OperationInfo and the OperationParameterInfo classes. In order to associate the generated contract with a Workflow Service we need to populate ServiceOperationInfo of the ReceiveActivity

Let's take the following sample workflow composed only by a ReceiveActivity.

Dynamic WF

The following code highlights how to create an OperationInfo instance that describes the contract implemented by the workflow. In our scenario the contract contains a single operation that receives two parameters as input. The code for generating the contract is included as part of the workflow constructor.

public sealed partial class Workflow1: SequentialWorkflowActivity

                {

        public string echoParam;

 

                                public Workflow1()

                                {

                                                InitializeComponent();

                                           CreateContract();

                                }

 

        private void CreateContract()

        {

            if (receiveActivity1.ServiceOperationInfo.Name != "TestOp")

            {

                OperationInfo opInfo = new OperationInfo();

                opInfo.ContractName = "DynamicContract";

                opInfo.Name = "TestOp";

                OperationParameterInfo param1Info = new OperationParameterInfo("param1");

                param1Info.ParameterType = typeof(int);

                param1Info.Attributes = ((System.Reflection.ParameterAttributes)((System.Reflection.ParameterAttributes.In)));

                param1Info.Position = 0;

                OperationParameterInfo param2Info = new OperationParameterInfo("param2");

                param2Info.ParameterType = typeof(int);

                param2Info.Position = 1;

                param2Info.Attributes = ((System.Reflection.ParameterAttributes)((System.Reflection.ParameterAttributes.In)));

                opInfo.Parameters.Add(param1Info);

                opInfo.Parameters.Add(param2Info);

                receiveActivity1.ServiceOperationInfo = opInfo;

            }

        }

 

        private void codeActivity2_ExecuteCode(object sender, EventArgs e)

        {

   Console.WriteLine(OperationContext.Current.RequestContext.RequestMessage.Headers.MessageId);

  Process the message...

        }

}

 

After that, we need to create an application that hosts the workflow using the WorkflowServiceHost class.

 static void Main(string[] args)
{
WorkflowServiceHost workflowHost = new WorkflowServiceHost(typeof(Workflow1));
workflowHost.Description.Behaviors.Find<WorkflowRuntimeBehavior>().WorkflowRuntime.WorkflowTerminated +=
delegate(object sender, WorkflowTerminatedEventArgs e) { Console.WriteLine("WorkflowTerminated: " + e.Exception.Message); };
workflowHost.Description.Behaviors.Find<WorkflowRuntimeBehavior>().WorkflowRuntime.WorkflowCompleted +=
delegate(object sender, WorkflowCompletedEventArgs e) { Console.WriteLine("WorkflowCompleted: " + e.WorkflowInstance.InstanceId.ToString()); };
workflowHost.Open();
Console.WriteLine("SequentialCalculatorService is ready.");
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Press <enter> to exit.");
Console.ResetColor();
Console.ReadLine();
workflowHost.Close();
}

 

Finally we need to associate the service with the corresponding WCF bindings.  Specifically on this case we are using the wsHttpContextBinding binding.

<configuration>

  <system.serviceModel>

    <services>

      <service name="WFDynamicContract.Workflow1" behaviorConfiguration="ServiceBehavior" >

        <host>

          <baseAddresses>

            <add baseAddress="http://myserver:myport/ServiceHost/Calculator.svc" />

          </baseAddresses>

        </host>

 

        <endpoint address=""

                  binding="wsHttpContextBinding"

                  contract="DynamicContract" />

      </service>

    </services>

 

    <behaviors>

      <serviceBehaviors>

        <behavior name="ServiceBehavior"  >

          <serviceMetadata httpGetEnabled="true" />

          <serviceDebug includeExceptionDetailInFaults="true" />

          <!-- Comment out the following behavior to disable persistence store -->

          <workflowRuntime name="WorkflowServiceHostRuntime" validateOnCreate="true" enablePerformanceCounters="true">

            <services>

              <add type="System.Workflow.Runtime.Hosting.SqlWorkflowPersistenceService, System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"

                   connectionString="Data Source=myserver;Initial Catalog=WFStore;Integrated Security=True;Pooling=False"

                   LoadIntervalSeconds="1" UnLoadOnIdle= "true" />

            </services>

          </workflowRuntime>

        </behavior>

      </serviceBehaviors>

    </behaviors>

  </system.serviceModel>

</configuration>

 

When we start the host the workflow constructor is called producing the following WSDL.

<wsdl:definitions name="WFDynamicContract.Workflow1" targetNamespace="http://tempuri.org/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:tns="http://tempuri.org/" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsap="http://schemas.xmlsoap.org/ws/2004/08/addressing/policy" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msc="http://schemas.microsoft.com/ws/2005/12/wsdl/contract" xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:wsa10="http://www.w3.org/2005/08/addressing" xmlns:wsx="http://schemas.xmlsoap.org/ws/2004/09/mex">

  <wsp:Policy wsu:Id="WSHttpContextBinding_DynamicContract_policy">

    Policy Info...

  </wsp:Policy>

  <wsdl:types>

    <xs:schema elementFormDefault="qualified" targetNamespace="http://tempuri.org/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://tempuri.org/">

      <xs:element name="TestOp">

        <xs:complexType>

          <xs:sequence>

            <xs:element minOccurs="0" name="param1" type="xs:int"/>

            <xs:element minOccurs="0" name="param2" type="xs:int"/>

          </xs:sequence>

        </xs:complexType>

      </xs:element>

      <xs:element name="TestOpResponse">

        <xs:complexType>

          <xs:sequence/>

        </xs:complexType>

      </xs:element>

    </xs:schema>

    <xsd:schema targetNamespace="http://tempuri.org/Imports">

      <xsd:import schemaLocation="http://localhost:8888/ServiceHost/Calculator.svc?xsd=xsd1" namespace="http://schemas.microsoft.com/2003/10/Serialization/"/>

    </xsd:schema>

  </wsdl:types>

  <wsdl:message name="DynamicContract_TestOp_InputMessage">

    <wsdl:part name="parameters" element="tns:TestOp"/>

  </wsdl:message>

  <wsdl:message name="DynamicContract_TestOp_OutputMessage">

    <wsdl:part name="parameters" element="tns:TestOpResponse"/>

  </wsdl:message>

  <wsdl:portType name="DynamicContract">

    <wsdl:operation name="TestOp">

      <wsdl:input wsaw:Action="http://tempuri.org/DynamicContract/TestOp" message="tns:DynamicContract_TestOp_InputMessage"/>

      <wsdl:output wsaw:Action="http://tempuri.org/DynamicContract/TestOpResponse" message="tns:DynamicContract_TestOp_OutputMessage"/>

    </wsdl:operation>

  </wsdl:portType>

  <wsdl:binding name="WSHttpContextBinding_DynamicContract" type="tns:DynamicContract">

    <wsp:PolicyReference URI="#WSHttpContextBinding_DynamicContract_policy"/>

    <soap12:binding transport="http://schemas.xmlsoap.org/soap/http"/>

    <wsdl:operation name="TestOp">

      <soap12:operation soapAction="http://tempuri.org/DynamicContract/TestOp" style="document"/>

      <wsdl:input>

        <wsp:PolicyReference URI="#WSHttpContextBinding_DynamicContract_TestOp_Input_policy"/>

        <soap12:body use="literal"/>

      </wsdl:input>

      <wsdl:output>

        <wsp:PolicyReference URI="#WSHttpContextBinding_DynamicContract_TestOp_output_policy"/>

        <soap12:body use="literal"/>

      </wsdl:output>

    </wsdl:operation>

  </wsdl:binding>

  <wsdl:service name="WFDynamicContract.Workflow1">

    <wsdl:port name="WSHttpContextBinding_DynamicContract" binding="tns:WSHttpContextBinding_DynamicContract">

      <soap12:address location="http://localhost:8888/ServiceHost/Calculator.svc"/>

      <wsa10:EndpointReference>

        <wsa10:Address>http://localhost:8888/ServiceHost/Calculator.svc</wsa10:Address>

        Identity Information....

      </wsa10:EndpointReference>

    </wsdl:port>

  </wsdl:service>

</wsdl:definitions>

 

As you can see the WSDL reflects the type information generated on the Workflow instance creation. Arguably, one of the drawbacks of this approach is that the service implementation can't take advantage of the object-xml serialization-deserialization processes because the contract is dynamically generated. On the other hands it is undoubtedly that there are a lot of scenarios that can be a great fit for dynamically generated contracts. Specifically, in WF this approach is a natural complement to the Workflow-First contract authoring style.

More Posts