An Architecture for Legacy Systems Integration

Much time and effort is required for a successful systems integration no matter which methods are available to you. Though integration can be difficult, data is often so valuable that you have no choice but to interact with a system, even if to extract only the data for a migration. Therefore, as you design a method of accessing data in these systems, you should attempt to do so with a method that will remain relevant and capable of supporting an organization's future needs. In this section we will show you an architecture that can be used to effectively implement and maintain legacy systems integration.

Core Criteria

Three criteria should be used to prepare legacy systems for integration. By making the system accessible through multiple communications mechanisms in advance, obsolescence by technology can be minimized. The following list outlines these three areas:

  1. The application should be capable of supporting multiple communications protocols such as HTTP, IIOP, SOAP, and others. While the first and immediate implementation can support only one, hooks should be built in to allow for future extension. The only exception is when you are building a one-time integration to migrate the data into a new system.
  2. The messaging interface should be protocol-independent and consistent.
  3. The data model used by the formatted messages should be defined in terms of the organization's business transactions.

While the first item simply provides for a flexible means to use any integration, the second criterion guarantees that applications can send and receive the same type of messages, formatted in the same way. Such flexibility is irrespective of the underlying communications mechanisms used and demonstrates why XML is perfect for this job. XML is preferred because it is platform-independent and is easy to work with due to the availability of a large number of tools for parsing, transforming, and processing XML documents.

The reason behind the third criterion is that any application already in a system is there to support a business process, a need, or both. Therefore, the interface to the integrated application should be defined using the terminology and artifacts of that process. By adhering to the third criterion the organization shields itself from the eventual replacement of the system as time goes on. In essence it prevents the system from becoming a "legacy system."

The Layered Approach

A legacy application can be prepared for integration through the implementation of a layered architecture pattern that allows for maximum flexibility. This pattern includes

  • A Transport Layer-The outermost layer is responsible for the receipt and return of XML-formatted messages through any given communications mechanism.
  • A Services Layer-The middle layer provides the functionality for interpreting XML-formatted requests, invoking the appropriate operations upon the legacy system, and formatting the responses into XML.
  • A Legacy Adapter Layer-The innermost layer is responsible for interfacing to the legacy system and represents the model of the data.

Regardless of the client type or communications mechanism employed, this architecture allows systems to perform business domain-level services via an XML-based interface. Imposing this method on top of a legacy system will enable the use of that system for years to come, assuming performance is not an issue.

Transport Layer

The Transport Layer, as we mentioned, allows clients of these legacy applications to use various protocols and mechanisms, such as HTTP, CORBA (IIOP), or DCOM, to invoke the services defined by the interface to the application. Classes in this layer can be fairly straightforward because their primary role is to provide a communications bridge to the actual services. The main responsibility of this layer is to make the rest of the system protocol-independent.

Listing 12-1, transport_layer.aspx, is a sample implementation of a Transport Layer that uses the HTTP protocol for invoking the services exposed by the ServiceLayer object defined in service_layer.cs.

Listing 12-1 transport_layer.aspx: Sample Transport Layer for HTTP protocol.

 <%@ import namespace="System.Xml"%> <script language="C#" runat="server"> /* string used to hold string representation of  our service request XML message */ String xmlStr = "<ServiceRequest>"; /* get names of all form variables into local array */ String names[] = Request.Form.AllKeys; /* loop through form variable to build request XML message */ foreach ( name in names ) { /* if form variable is called "service", use its value to  build the ServiceName element */ if ( name == "service" ) { xmlStr += "<ServiceName>" + name + "</ServiceName>"; } /* othewise use this form variable's name and value to  build a <Param> element */ else { xmlStr += "<Param name=\"" + name + "\">" + Request.Form[ name ] + "</Param>"; } } xmlStr += "</ServiceRequest>" /* create XmlDocument using the reqXML string */ XmlDocument xmlDoc = new XmlDocument(); xmlDoc.LoadXml( xmlStr ); /* instantiate our ServiceLayer object */ ServiceLayer service = new ServiceLayer(); /* invoke the perform() method with our request XML message */ XmlDocument retXmlDoc = service.perform( xmlDoc ); /* output the response XML message to the client tell  browser/client that we are outputting XML */ Response.ContextType = "text/xml" /* output the response XML message */ Response.Write( retXmlDoc.outerXml ); </script> 

In this implementation of a Transport Layer, we first built our request XML message from HTTP POST variables. We then instantiated an instance of the ServiceLayer object developed in service_layer.cs and invoked its perform( ) method with the request XML message as its parameter. Finally all that was left was to display the resultant response XML message to the client.

Services Layer

The second layer defines how the services in the system are invoked and how the data returned by the services is formatted. XML-formatted requests are forwarded to this layer via the Transport Layer, and the classes in this layer parse the transaction requests and invoke the appropriate operations on the legacy system.

The methods exposed by the Legacy Adapter Layer and the parameters used in invoking those methods should be used to design the format of the XML messages used by this layer. When designing the format of these messages, keep it consistent across the different services to be invoked. For example, Listing 12-2, sample_request.xml, shows a sample XML message that can be used to invoke the services exposed by the LegacyLayer object defined in legacy_layer.cs.

The ServiceName element is used to indicate the legacy service to invoke. The <Param> elements hold the names and values of the parameters that are used when invoking a particular service; thus the same format can be used to invoke services of different calling conventions.

Listing 12-2 sample_request.xml: Sample service request XML message.

 <?xml version="1.0"?> <ServiceRequest> <ServiceName>Order</ServiceName> <Param name="customerID">foobar</Param> <Param name="itemID">7843221</Param> <Param name="qty">5</Param> </ServiceRequest> 

Listing 12-3, service_layer.cs, is an example of the implementation of a Service Layer that uses the XML message defined in sample_request.xml to invoke legacy services.

Listing 12-3 service_layer.cs: Sample Service Layer.

 using System.Xml; public class ServiceLayer { public XmlDocument perform( XmlDocument params ) { /* our Legacy Layer object */ Legacy legacy = new Legacy(); /* retrieve name of the legacy service to invoke */ DocumentNavigator nav = new DocumentNavigator( params ); nav.Select( "//ServiceRequest/ServiceName" ); nav.MoveToNextSelected(); serviceName = nav.innerText; /* string used to hold result of the method invocation */ string res = ""; switch ( serviceName ) { case "Order" : /* retrieve the customerID, itemID, and qty parameters  from the Params XML document */ nav.Select( "//ServiceRequest/Param[@name='customerID']" ); nav.MoveToNextSelected(); string customerID = nav.innerText; nav.Select( "//ServiceRequest/Param[@name='itemID']" ); nav.MoveToNextSelected(); string itemID = nav.innerText; nav.Select( "//ServiceRequest/Param[@name='qty']" ); nav.MoveToNextSelected(); string qty = nav.innerText; /* invoke the orderItem() method in our Legacy object  to place an order and build result XML document based on  the outcome. */ try { string tranID = legacy.orderItem( customerID, itemID, qty ); res = "<TransactionID>" + tranID + </TransactionID>"; } catch ( Exception e ) { res = "<Fault>" + e.Message + </Fault>"; } break; case "History" : /* retrieve the customerID parameter from the Params  in the XML document */ nav.Select( "//ServiceRequest/Param[@name='customerID']" ); nav.MoveToNextSelected(); string customerID = nav.innerText; /* invoke the getOrderHistory() method in our Legacy  object and output result in our resultDoc XML document */ try { string[] tranIDs = legacy.getHistory( customerID ); /* get list of transaction IDs of past transaction  and build result XML document */ foreach ( string tranID in tranIDs ) { res = res + "<TransactionID>" + tranID + "</TransactionID>"; } } catch ( Exception e ) { res = "<Fault>" + e.Message + </Fault>"; } break; } /* build and return result XML document */ XmlDocument resultDoc = new XmlDocument(); resultDoc.loadXml( "<ServiceRequestResponse>" + res +  "</ServiceRequestResponse>" ); return ( resultDoc ); } } 

The ServiceLayer object exposes a single method called perform( ) that accepts an XmlDocument argument which is used to determine the name of the legacy service to invoke and their input parameters. The perform( ) method returns an XmlDocument object containing the results of this invocation.

ServiceLayer supports two services, the Order that is used to place an order and the History that is used to determine the transaction history of a specific customer. Note that the implementation of these services are handled by the respective orderItem( ) and getOrderHistory( ) methods in the LegacyLayer object in legacy_layer.cs that we developed in the last section.

Legacy Adapter Layer

The innermost layer contains all the code necessary to interact with the legacy system. Using one of the methods outlined in the "Creating Interfaces to Legacy Systems" section is suggested. Business-level behaviors and rules encapsulated by the legacy system are exposed to the Services Layer, so this layer does not need to know anything about XML.

When designing services for this layer you need to have the correct granularity. In building distributed systems, designers commonly specify interfaces that are too finely grained. These designs do not scale well, and clearly defining and managing transactional boundaries is difficult. For example legacy_layer.cs, shown in Listing 12-4, contains the pseudocode of a LegacyLayer object that exposes two methods for placing orders and inquiring about previous transaction history.

Listing 12-4 legacy_layer.cs: The psuedocode of a sample Legacy Layer.

 public class LegacyLayer { /* place an order return the new transaction ID or throw  an exception if an error occurred. */ public string orderItem( string customerID, string itemID, string qty ) { /* insert your actual implementation here... */ } /* get order transaction of a specific customer. return a  string array containing the list of the transaction IDs  of previous order transaction or throw an exception if  an error occurred. */ public string[] getOrderHistory( string customerID ) { /* insert your actual implementation here... */ } } } 

Note that these methods deal with native data types directly instead of XML messages. Also note that the granularity and stateless nature of the methods can simplify transaction management as well as improve scalability.

Availability and Scalability Considerations

One common approach for tackling the availability and scalability issues we discussed that are associated with integrating legacy systems is to decouple the legacy systems from the new application by using a messaging server such as the MSMQ or IBM's MQSeries. Using a messaging server allows the two separate subsystems to operate at their own pace.

On the application side you can concurrently send thousands of requests and obtain excellent performance from a messaging server. You can process messages at a pace the legacy system can handle, and therefore, can maximize both systems without overloading either. For example, if only five connections are available through IBM Systems Network Architecture (SNA), you can process five messages at a time. This allows you to throttle the load on the legacy system while at the same time addressing your availability concerns and allowing your application to scale appropriately.



XML Programming
XML Programming Bible
ISBN: 0764538292
EAN: 2147483647
Year: 2002
Pages: 134

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net