Last month, I discussed the real problem with legacy applications. It's not that these applications are old, nor is it that they are no longer in line with your organization's needs. Certainly, these are issues, but they wouldn't be critical issues if your legacy applications were easy to modify. Therefore, the real problem with legacy applications (and the reason they became "legacy" applications in the first place) is that these applications are difficult to change. Last month, I also discussed the general philosophy of how to optimally migrate legacy applications onto flexible, scalable environments so that the applications would be easy to grow and change as future needs dictated. The three main concepts I outlined were that all components of the target environment must be scalable, the migration must occur incrementally and the application itself should be partitioned into smaller functional units.

The first concept is easy. It just means you have to make sure you use scalable hardware and database software. You can satisfy that requirement by simply writing a check to the vendors of your choice. The other two requirements aren't as easy. They require design work and development time. They require that you use a technique that is tailored to achieving incremental migration. This month, I'll cover the basic steps of that technique.

The Technique

To migrate a legacy application incrementally, you must think of the current application in layers. Though three-tier applications are all the rage these days (and it's likely that your target architecture will be three-tier), most legacy applications weren't built that way. Instead, they were usually built as two conceptual layers: the database layer (responsible for the storage, manipulation and maintenance of the data) and the application layer (responsible for processing the data and for presenting a user interface). These two conceptual layers were often implemented on a single physical tier--the mainframe. However, even though they're often on the same tier doesn't mean they're always physically organized the same way. Generally, these two layers either exist as physically separate layers (which is generally the case with the more "modern" legacy applications) or they are intimately intertwined (with no real separate DBMS and, therefore, no concept of data independence). The approach you follow in either case is the same, but unfortunately the complexity increases greatly in the case where the layers are intertwined. Let's first look at the case where your layers are relatively separate, and look at the basic steps of a migration.

Step 1: Document the Legacy System

Often, the first hurdle to overcome is to understand how the legacy system is set up. Frequently, little documentation exists about how the system truly works. So, the first step is to understand what it is that you're starting with. Though it's useful to have detailed documentation of the entire system, it's rarely feasible to create this documentation if it doesn't already exist. So, for the purposes of a migration, the main focus of this step should be to build a high-level block diagram of the existing system, showing all the hardware platforms, where all the data is located and what functions occur on each platform.

Step 2: Partition the Application

If the resulting application is to be scalable, then the application must be partitioned into smaller functional components. If the application is built monolithically (that is, it's written as one large executable), then it will not be able to take advantage of multiple processors to increase its scalability. If it's a single executable, then it will run as a single process, and a single process can only run on a single processor. So, to achieve scalability, you must partition the application into separate functional components that can be run as separate processes.

For an example, let's look at a transportation company I worked with. Their original system was designed as a single application that performed four main functions: 1) take orders for pickup of goods to be transported, 2) perform scheduling optimization, 3) track shipments, and 4) bill the customer. This application, along with the database, all ran on a mainframe. However, the mainframe had become unable to handle the recent increases in workload, so the client wanted to migrate to a scalable platform. The database remained on the original mainframe, but the application was divided into four components (one for each main function), and the various pieces of the application code were placed on newly purchased application servers. A "request broker" was also added to the system, and its job was to receive all client requests and direct them to the appropriate application server based on the request type.

For partitioning to work, each component must have a well-defined abstract interface. For example, components cannot have direct procedure calls that manipulate the internal structures of other components. This is the basis of all sound development principles, and the benefit is that it allows easier modification in the future. It allows you to change one component without needing to change any of the others.

To determine how to partition the functionality, you need to know how all the pieces of functionality currently interact. This can be done with code analyzers (which show the dependencies between function and procedure calls), by talking to the IT staff and by actually looking at the source code yourself.

Step 3: Incrementally Migrate the Functional Partitions

Once you have defined how you are going to partition your application into multiple components, you can then migrate the first component. Migrating all the components at once is a recipe for failure, because the project becomes too big. But, migrating a piece at a time does introduce some new requirements. If only a piece is migrated at a time, that implies that both the new environment and the old environment must coexist for a while. Sometimes, there are good reasons to leave some of the functionality on the legacy platform, implying that the old and the new environments might even have to coexist indefinitely.

Regardless, this coexistence requires the use of two different types of gateways. "Forward" gateways allow existing legacy application code to access data that has been moved to the target platform. "Reverse" gateways allow new application code to access data that still resides on the legacy platform. These gateways have three main functions. First, they receive requests from the clients, and they route the request to the appropriate platform based on the requested function. Second, they translate requests to the appropriate format depending on where the request is being sent. And third, they transform the returned data into the format that the requesting client expects. The purpose of these gateways is to insulate the two conceptual layers (the application and the database) from each other so that changes in one do not affect (or only minimally affect) the other. (For an excellent in-depth discussion of gateways, I recommend Migrating Legacy Systems, a book by Michael Brodie and Michael Stonebraker.)

Gateways can sometimes be expensive to build or buy, and sometimes they will even become obsolete once the entire application is migrated. But, from real-world experience, they are the only feasible approach. They allow you to incrementally migrate an application, which greatly reduces your risk.

Step 4: Test for Scalability

Each time you migrate a component, test it for scalability. Simulate different workloads, different numbers of users, different amounts of data, etc. If there are bottlenecks, go back to the drawing boards and re-architect those portions that have bottlenecks. Only once you are convinced that the component scales should you release it to the end users.

Intertwined Conceptual Layers

The previous discussion assumes that the application layer and the database layer are fairly separate. What happens when this isn't the case? In these scenarios, there really is no separate DBMS. The application directly stores, manipulates and maintains the data. Unfortunately, there are no wonderful solutions here. Your task will necessarily be more difficult. It is more difficult to isolate changes in one section from affecting others, and the tangible result is that your incremental migration will have larger increments that encompass more functionality, and your gateways will be more complex. These larger increments mean that these projects will necessarily be riskier. But, for very large applications, it is still far less risky than trying to migrate the entire application at once.

A Dynamic Environment

Once your application has been migrated to a scalable platform and once it has been partitioned into functional components, you now have an environment that should not become your next legacy application. Your new environment is scalable, which means it can grow as your needs grow. Also, because all functionality is designed in a component fashion, the system can adapt to meet new functionality needs by adding in a new functional component. The result is an environment that is as dynamic as your own organization.

Register or login for access to this item and much more

All Information Management content is archived after seven days.

Community members receive:
  • All recent and archived articles
  • Conference offers and updates
  • A full menu of enewsletter options
  • Web seminars, white papers, ebooks

Don't have an account? Register for Free Unlimited Access