“Java Monoliths” – Modernizing an Oxymoron

Jason Bloomberg

March 1, 2022

Use AI and Cloud-Native Principles for Selective Modernization

In the late 1990s, Java quickly proved invaluable for building applications that depended on n-tier architectures to deliver web sites at scale. The web wouldn’t be what it is today if it weren’t for the power of Java Enterprise Edition (Java EE).

Today, those massive object-oriented applications of the 1990s and 2000s are themselves legacy – and all those Java EE monstrosities have become today’s monoliths.

The irony in this situation should not be lost, especially for those of us who lived through the Web 1.0 days. 

Object orientation promised all the benefits of modularity at scale – but now, complex interdependencies and questionable architectural decisions counteract any advantages that modular code might have promised.

Despite Java EE’s modularity and encapsulated business logic, its package-based deployment model remained monolithic. Today, Java EE apps have become an oxymoron – the antithesis of their original vision of modularity.

Are we stuck, then, with these creaking Java monoliths, nothing more than impenetrable spaghetti?

The good news: the answer is no. It’s possible to modernize legacy Java code in such a way that preserves what value remains while moving to a new paradigm of modularity based on microservices: cloud-native computing.

If it Ain’t Broke, Don’t Fix it

The first principle of legacy modernization, Java monolith or not: if it ain’t broke, don’t fix it.

Just because some code is old, runs on older infrastructure, or was written in an out-of-date language, doesn’t automatically mean that you should replace it.

Instead, the modernization team should take a close look at legacy assets in order to sort them into four categories: reuse, refactor, rewrite, or replace.

Business priorities should drive the decisions on how to sort various software objects. What value does the code provide today? What value does the business expect from its modernization? What is the cost of the modernization, and is a particular approach cost-effective?

Sometimes it makes the most sense to leave code in place (reuse). In other cases, transitioning from legacy to modern code without changing its functionality meets the business need (refactor).

In other cases, fixing existing code simply isn’t practical, in which case rewrite or replace is the best choice.

Resolving Dependencies

Well-written Java code was certainly modular, but its complicated inheritance and instantiation principles easily led to a proliferation of interdependent objects and classes.

Mix in the multiple tiers that Java EE supported, and the interdependencies soon became intractable “spaghetti code” messes. Any successful Java modernization project must untangle these messes in order to come up with a plan for cleaning them up.

Dynamic and static analysis techniques that identify the appropriate groupings of functionality that will reduce interdependencies is essential for understanding the complex interconnections within legacy Java applications.

vFunction uses graph theory and machine learning-driven clustering algorithms to automatically identify such groupings. The goal is to identify target microservices that fall into optimized business domains.

Business domains, in fact, are central to cloud-native architecture, as they provide the boundaries between groups of interconnected microservices.

In other words, cloud-native architectures introduce modularity at a level that is more coarse-grained and thus more business-focused than the modularity at the object level that Java and other object-oriented languages support.

Note, however, that these two types of modularity work hand in hand. Modern microservices might be written in Java, .NET or other object-oriented languages, and thus a single microservice might contain multiple such objects. 

Iterating the Architecture

vFunction also discovers interdependencies among database tables and the various objects and services that access them. The platform further optimizes service decomposition accordingly.

Once a platform like vFunction exposes interdependencies and recommends groupings of target microservices, it’s up to the architects to iteratively fine-tune the architecture. 

This human touch further minimizes dependencies and optimizes the exclusivity and cohesion of target microservices – in other words, ensuring that each microservice does one thing and one thing well.

Architects are also able to refine the modernization plan following the first rule of modernization above – deciding which parts of the code the team should reuse, refactor, replace, or rewrite.

Architects can handle these tasks entirely within the vFunction platform UI, taking advantage of the insights that the platform’s machine learning have provided into the optimal organization of microservices into business domains.

Building the Target Microservices

When the modernization effort calls for new or refactored microservices, the vFunction platform specifies those services in JSON format, essentially providing a recipe for developers to follow as they create modern Java microservices (typically using Spring Boot) that refactor or replace existing functionality as necessary.

This microservice specification also aids in testing, as it represents the functionality that the modernization analysis has identified. In other words, the specification feeds the test plans that ensure that modernized functionality behaves as required.

Just as architects will continue to iterate on the architecture, developers should continue to iterate on creating and updating microservices. 

Modernizing a monolith is itself not a monolithic task. It’s essential for the entire team to look at modernization as an ongoing process that continues to discover previously unknown interdependencies that lead to selective refactoring and rewriting as the overall cloud-native microservices architecture continues to mature.

The Bottom Line     

Modernizing legacy Java monoliths by creating modern microservices as part of a cloud-native architecture deployment may represent the entire modernization effort for some organizations – but more often than not, this effort is only one facet of a more complex modernization strategy.

A typical large enterprise, for example, may have many different instances of legacy technology, including mainframe and client/server applications that may consist of many different languages.

A successful enterprise modernization strategy, therefore, must invariably focus on the business benefits of modernization, where the team carefully balances benefits with the significant costs and risk of such initiatives. 

Adding to this challenge is the fact that modernization is always a moving target as business requirements change and available technologies and best practices continue to evolve. 

Calculating a firm return on investment for modernization, therefore, can be a difficult task – but don’t allow the challenges with measuring its business benefits get in the way of getting started.

Copyright © Intellyx LLC. vFunction is an Intellyx customer. Intellyx retains final editorial control of this article.

Jason Bloomberg

Get started with vFunction

See how vFunction can accelerate engineering velocity and increase application resiliency and scalability at your organization.