Why Java EE Is A Bad Fit For Cloud-Native Applications
Markus EiseleDirector Developer Advocacy, Lightbend, Inc.
Cloud Native And The Future Of Java EE
By 2019, fewer than 35% of all new business applications
will be deployed in Java EE application servers.
Global 5000 enterprises that never before considered themselves as technology companies are now faced with digital business imperatives that force them to modernize their infrastructure. On the path to becoming a digital, on-demand provider, development speed is the ultimate competitive advantage. With technology and business innovation inextricably intertwined, businesses must adapt with greater agility than ever before.
Technology leaders (e.g. Amazon, Microsoft, Google, LinkedIn, etc.) and industry analysts (Gartner, Forrester Research, RedMonk) agree that modern system architecture must embrace a cloud-first strategy to capture the benefits of development agility and cost efficiency. Modern systems need to be optimized to reduce latency and architected for resilience and elasticity. As online consumption continues to grow at an exponential rate, modern systems require a highly flexible infrastructure design that can scale at levels far higher than previous conceptions of peak traffic.
For the majority of use cases, however, the Global 2000 do not have the luxury of starting with a greenfield infrastructure such as digital natives LinkedIn, Netflix, or Airbnb. Changes need to be made within existing frameworks to keep pace with new web-scale organizations. This presents challenges to many organizations that have huge investments in legacy Java EE infrastructure, where technical debt and monolithic system architectures require modernization in order to confront the following business risks:
Development agility isn’t meeting business demands and causes slow and infrequent releases
Monolithic applications that are difficult and expensive to scale and aren’t optimized for cloud infrastructure
Batch approaches reduce the ability to react based on real-time insights and streaming ‘data in motion’
Complexity kills development velocity and only fosters infrequent releases
For a decade or more, enterprise development teams have built their Java EE projects inside large, monolithic application server containers without much regard to the individual lifecycle of their module or component.
Hooking into startup and shutdown events was simple, as accessing other components was just an injected instance away. It was comparably easy to map objects into relational databases or connect to other systems via messaging. One of the greatest advantages of this architecture was transactionality, which was synchronous, easy to implement, and simple to visualize and monitor. Projects were released twice per year, had multi-year lifespans, multi-month test cycles, and large teams to manage everything.
But those days are now at an end. Due to the lack of agility, applications grew enormous, leading to the accumulation of technical debt, slower development velocity, and longer release cycles. Here's how
Development team agility is constantly blocked. With no simple development model to support modern systems, the traditional compile-build-deploy cycle for every service cripples productivity. At best, reusability and componentization in Java EE are achieved by sharing packaged bundles between projects. These designs ultimately rely on a single database schema with sheer project ROI calculated on comparably long production uptimes.
Big teams and heavy apps create long release cycles. With ongoing maintenance and the constant addition of new features, monoliths constantly grow in size. This means that the burden each team incurs to create, maintain, and manage their app continuously increases, slowing productivity. Team structures are also heavily influenced by these monolithic software architectures, with multi-month test cycles being perhaps the most visible proof. Projects with life spans longer than five years tend to have huge bug and feature databases. Since containerless development is impossible, testing is barely qualified—there are no acceptance tests and hardly any written business requirements or identifiable domains in design and usability.
Complex code bases and fearful engineers lead to technical debt. Instead of business-driven components, the classical monolith has a very technical design and struggles to keep up with the constant change in business requirements. Production releases often occur only twice a year, and introducing new features or making hotfixes outside of the official production setting process is a risky venture. Upholding the motto: “Never change a running system,” continuous inherited complexity leads to a very cautious update process by engineers who are understandably afraid of breaking anything.
Scaling monoliths is too expensive for the cloud
From a production perspective, the classical monolith relies on heavyweight infrastructure and rarely scales inside the application server itself. Scaling, therefore, requires vast engineering resources, making it a clunky, expensive, and inefficient process.
Monoliths are difficult and expensive to scale. Java EE applications are bound to the thread-per-request model, making it difficult for them to scale to larger numbers of nodes. And clustering relies on vendor-specific features because it’s not part of the Java EE specification. Servicing a growing number of users requires the complete replication of the application server stack, including the underlying infrastructure. With more extensive scaling requirements, the use of vendor-proprietary features and clustering options became mandatory for many installations.
Monoliths lead to resource inefficiency. Even with optimizations, scalability is limited to a couple of hundred nodes and can’t be controlled dynamically to serve load peaks without confronting failures. Instead of intelligent, dynamic scaling, monoliths must always be prepared for traffic peaks. This makes scaling and calculating infrastructure requirements difficult, leading to a lot of unused compute power that is only activated for rare high demand occasions. And when things go wrong, application servers have few built-in resilience mechanisms—one component failure is propagated instead of contained and usually brings down the entire application.
Separate cloud-native operational models from application architecture and developer APIs
Java EE provides two fundamental capabilities: (1) an application architecture with supporting APIs and (2) a multi-application operational runtime. Modern cloud native solutions separate these capabilities, greatly increasing development team flexibility and agility. Operational models are provided by orchestration solutions (e.g. Kubernetes) or by cloud solutions. Application architectures and developer APIs must enable cloud native operations by delivering the elasticity and resilience requirements of cloud applications.
Streams and ‘data in motion’ need to be supported
Since its invention, the way people use the internet has fundamentally changed. A tidal wave of connected devices, sensors, and intelligent home appliances has caused demands to grow exponentially.
In 1995, less than 1% of the world population had an internet connection. Today it’s around 40%. The number of internet users increased tenfold from 1999 to 2013. The world’s billionth user logged on in 2005, and by 2010, that number had doubled to two billion. In 2018, the internet reached four billion users. In response to this rampant growth, the business requirements for modern applications have drastically changed:
Real-time streaming data is a first-class citizen in today’s applications. Instead of operating on data that rests in a centralized relational database (RDBMS), modern software increasingly relies on data processing in near real-time. With the shrinking demand for batch-mode processing, the ability to work with time-sensitive data presents an enormous competitive business advantage. Yet Java EE has no native support for streaming “data in motion” technologies like Akka Streams, Apache Spark, etc., and the persistence tools provided (JDBC and JPA) are synchronous and blocking, allowing only one query at a time per connection.
Insights and value must be harvested from data. Modern reporting and incident analysis must happen as the data streams into the application, not in retrospective. With high flexibility now a requirement rather than a “nice to have,” production systems must be equipped to resolve issues that weren’t considered or even relevant when their initial version was put into production. With the help of message-driven systems and their built-in event-log capabilities, it is now possible to not only change the processing of incoming data quickly but also to replay previously captured data-sets and extend with new analytics or actions.
Non-traditional data persistence models must be used. Handling of large amounts of data within distributed systems requires immutability for data, and immutable deployment units surrounding services are the new paradigm for load balancing, high availability, and dynamic resource sharing. While Java EE provides JPA, which works well with classical RDBMS based persistence, only Command Query Responsibility Segregation (CQRS) and Event-Sourcing (ES) work well with immutable data-structures and are beneficial for microservices that handle streaming data.
The Bottom Line
Over the years, the use of legacy technologies and expensive Java EE middleware servers has resulted in the pervasiveness of large, monolithic applications. Enterprises are becoming bogged down with long release cycles and increasingly complex applications, leaving teams unable to achieve a high level of development productivity as well as firefighting production systems with an unhealthy amount of interdependencies that were never designed for cloud infrastructures.
This is where we can help. Lightbend helps developers create applications that are responsive, resilient, flexible, and message-driven. Built on a foundation of Domain Driven Design and based on services rather than plain objects, Lightbend provides the perfect architecture for creating powerful, adaptable applications that thrive in the cloud.