Case Study: Refactoring A Monolith Into A Cloud-Native App (Part 1)

August 27, 2015 Jared Gordon

 

sfeatured-clouds-monolithsOn their Cloud-Native journey, many companies are challenged with moving legacy apps to next-gen, Cloud-Native platforms such as Pivotal Cloud Foundry. This is the first part of a series of blog posts covering the migration and refactoring of an existing monolithic application into a federation of related microservices.

Typically, the topic of migration comes up when our clients begin triaging their application portfolios to prioritize what gets moved over to their newly acquired Cloud-Native application platform. These companies have a platform, they have their apps, now they need to figure out how to put the two together effectively.

There are numerous online examples that demonstrate how to create greenfield, cloud-enabled applications using a variety of languages and approaches. But, what about the “typical” enterprise use case, where applications are:

  • Large-scale monoliths
  • Internally tightly coupled
  • Tied to specific containers, frameworks, and/or libraries, and
  • Not easily be re-written from a cost or risk perspective

If we can’t just rewrite the app, “lift and shift” might seem like a good approach. Just move the application over as-is onto the new platform!

Realistically, it is not usually possible to directly migrate an application over to the cloud, and there can be potentially extensive modifications. And, even if it can be done, moving an application over as-is means bringing over its existing challenges. This doesn’t prepare it for the advantages of a Cloud-Native architecture. Josh Kruck discusses this in his article How Do I Migrate Applications to Pivotal Cloud Foundry?, and we’ll be building upon these points via practical examples.

The Series Roadmap

Over the course of this series, we will be taking a legacy application and evolving it into a Cloud-Native application. This will involve:

  • Getting it up and running
  • Identifying candidate components to be transformed into microservices
  • Linking these services together using the tools provided by NetflixOSS

We will also discuss the motivation behind the modifications we make and provide justifications for why we took the direction we took. The overall goal is demonstrating a middle road between “complete re-write”, and “just move the application as is.”

To kick things off, this first post focuses on getting an older application up and running in a new environment. In the case of our sample application, we were able to get it running on a Cloud-Native platform without a major refactor, albeit bringing some legacy baggage along. We will start to jettison this baggage as we progress through the series.

Editor’s Note: Check out part 2 in this series, focusing on migrating an existing monolithic application to a federation of related microservices.

The Target Legacy App

To help make this exercise concrete, we have chosen an existing application written using technologies that should be familiar to many enterprise shops:

  • Java
  • Spring
  • Hibernate
  • JMS
  • Web Services

Our subject application, SpringTrader, can be accessed here:
https://github.com/cf-platform-eng/springtrader-cf

SpringTrader is a demonstration application originally written as a showcase for various Spring technologies. Over time, it has been updated and re-platformed by many contributors. Recently it was forked and refactored yet again to get it runnable on an earlier version of Pivotal Cloud Foundry (as a lift-and-shift project). Time has passed, bit-rot has taken hold, and the application no longer builds with current JDKs, nor runs on current versions of Pivotal Cloud Foundry, our target application platform.

Lots of hands, lots of different technologies, stuck in place, architecturally entropic. But, it was providing value and could again. Sound familiar?

Some Preliminaries

Before we get to the details, we plan to:

  • Provide Justifications: Enterprise applications operate within business contexts, and realistically we need to justify our technical changes based on business needs.
  • Explain Why: The series of blog articles will be descriptive and will discuss the challenges faced, how they were overcome, and why a particular course of action was chosen.
  • Explain How: We will create git branches to cover the changes discussed in each blog. The README for each of the branches will elaborate on the technical nitty-gritty (code, library changes, diffs, etc.).

Methodology:

Throughout, we will be following an agile approach

  • Things will be done via small, incremental changes
  • We will make sure we have adequate test coverage to validate these changes
  • We will make sure that we still have a working product after every round of change

Documentation:

A natural way to think about how an application changes over time is to study its stream of code commits. Github supports this via its diff views, and we’ll make use of these to summarize the migration.

Let Us Proceed—Getting Our App Back On Its Feet

Great, with all of that out of the way, we can dive into the first phase. Remember, our first goal is simply to get the current codebase running on a Cloud-Native architecture. We pick up SpringTrader at a specific point of time, declaring that our migration begins right after this commit:
https://github.com/cf-platform-eng/springtrader-cf/commit/ef9a5d21289a4e056c251b6b91ae6fa8b902b5b6

There’s a branch in github, representing this baseline:
https://github.com/cf-platform-eng/springtrader-cf/tree/baseline

The README for the baseline goes into a bit of detail about where things stand, how to build the project and so forth. Since this is our baseline, there is not much to say at the detailed level—we’ve not made any changes yet.

We’ll start out with some very easy modifications to get things rolling.

Justification

As it stands, the application will not build. If it can’t build, we can’t modify it, test it, or deploy new versions of it. We need to get this app turned around or it will be of no future business use at all. With this in mind we will focus our immediate efforts on getting the app running again.

Goals

Do the bare minimum to:

  • Get it built
  • Get the tests to pass
  • Get it deployed
  • Get it running

Solution

To see what was done to get through this round of changes, please refer to the diff:
https://github.com/cf-platform-eng/springtrader-cf/compare/baseline…part1

For more details than cannot be covered here in the blog, please see the README:
https://github.com/cf-platform-eng/springtrader-cf/tree/part1

At a high level, here are the changes we made to the application:

  • Updated the README.md file
  • Changed from JD6 to JDK7 and targeted a specific buildpack on Pivotal Cloud Foundry
  • Edited the deployApp.sh and the deleteDeployment.sh file
  • Edited the build.gradle file

Discussion of Issues

Application Tied to JDK 6

JDK 6 went out-of-support in early 2013. Thankfully, the SpringTrader build is based on Gradle, and we can make use of its dependency management features going forward. If the build was not based on either Gradle or Maven, the first order of business would have been to refactor the build process, a potentially major undertaking. In our case, upgrading to JDK 7 was a simple matter of changing some build flags and being sure to use a JDK 7 compiler.

But, hang on, didn’t JDK 7 go out-of-support in April, 2015? Why not go to JDK 8? It turns out that there are existing dependencies that are incompatible with JDK 8. We’re not ready to make the changes needed to jump from JDK 6 directly to JDK 8 yet. We will need to tackle this at a later date.

Library Incompatibilities

There is also a lot that could be done to improve the dependency stack. To start with, the build explicitly declares dependencies and versions. Current best practices point to the use of “starter” or “parent” -based dependency management, leaving the details of specific versions and transitive dependencies to these meta-libraries.

Beyond this, the build uses some pretty out-of-date library versions. Unlike wine or cheese, software does not improve with age. Bringing these libraries up to date might reduce the application’s exposure to latent bugs or security vulnerabilities. It is very tempting to clean all of this up, but not yet. We are going to stick to the minimum changes necessary to reach our current goal.

Where We Stand at the End of Part 1

After the minor changes stated above, the app can be built and deployed to Pivotal Cloud Foundry, and we have met our immediate goals.

Summary:

We started with something simple:

  • Updating the build to a new JDK
  • Updating the deployment scripts to deal with changes in the Java buildpack and remove some obsolete usages
  • Purposely NOT undertaking major changes in the library stack at this time

We also identified some technical debt:

  • Need to upgrade from JDK 7 to JDK 8
  • Need to refactor dependency management and revisit library versions

It’s a start.

Next Steps

Now that we’ve established a working foundation we can begin to break the monolith up into smaller chunks. Stay tuned.

Learning More:

 

About the Author

Biography

More Content by Jared Gordon
Previous
Using Data Science To Save And Improve Lives
Using Data Science To Save And Improve Lives

Data science is actively being used to save lives and improve healthcare. In this post, Principal Data Scie...

Next
That Cloud-Native Lifestyle
That Cloud-Native Lifestyle

In this episode, Andrew Clay Shafer is back for a discussion with Coté on what Cloud Native means. The pair...

Enter curious. Exit smarter.

Register Now