There are many challenges related to building and architecting a web application, especially if you want to keep your codebase updated with modern techniques without throwing away a lot of your code every two years in favor of the latest trendy JS framework.
In our team, we can successfully keep a 7+ year-old Java application up-to-date, combining modern techniques with a legacy codebase of more than 1 million LOC, with an agile, sustainable, and evolutionary web approach.
More than just choosing and applying any web framework as the foundation of our web application, we based our web application architecture on 5 architectural pillars that proved crucial for our platform’s success. This post is a first in a series, that will discuss exactly that.
In the first week of October, Alex Porcelli and I had the opportunity to present at JavaOne San Francisco 2017, the two sessions we held related to our work: 5 Pillars of a Successful Java Web Application and The Hidden Secret of Java Open Source Projects.
It was great to share our cumulative experience over the years building the workbench and the web tooling for the Drools and jBPM platform and both sessions had great attendance (250+ people in the room).
In this series of posts, we’ll detail our "5 Pillars of a Successful Java Web Application”, trying to give you an overview of our research and a taste of participating in a great event like Java One. Let's talk about them:
1st Pillar: Large Scale Applications
The first pillar is that every web application architecture should be concerned about the potential of becoming a long-lived and mission-critical application, or in other words, a large-scale application. Even if your web application is not exactly big like ours (1mi+ lines of web code, 150 sub-projects, +7 years old) you should be concerned about the possibility that your small web app will become a big and important codebase for your business. What if your startup becomes an overnight success? What if your enterprise application needs to integrate with several external systems?
Every web application should be built as a large-scale application because it is part of a distributed system and it is hard to expect what will happen to your application and company in two to five years.
And for us, a critical tool for building these kinds of distributed and large-scale applications throughout the years has been static typing.
Static Typing
The debate of static vs. dynamic typing is very controversial. People who're advocates of dynamic typing usually argue that it makes the developer's job easier. This is true for certain problems.
However, static typing and a strong type system, among other advantages, simplify identifying errors that can generate failures in production and, especially for large-scale systems, make refactoring more effective.
Every application demands constant refactoring and cleaning. It’s a natural need. For large-scale ones, with codebases spread across multiple modules/projects, this task is even more complex. The confidence when refactoring is related to two factors: test coverage and the tooling that only a static type system is able to provide.
For instance, we need a static type system in order to find all usages of a method, in order to extract classes, and most importantly to figure out at compile time if we accidentally broke something.
But we are in web development and JavaScript is the language of the web. How can we have static typing in order to refactor effectively in the browser?
Using a transpiler
A transpiler is a type of compiler that takes the source code of a program written in one programming language as its input and produces equivalent source code in another programming language.
This is a well-known Computer Science problem and there are a lot of transpilers that output JavaScript. In a sense, JavaScript is the assembly of the web: the common ground across all the web ecosystems. We, as engineers, need to figure out what is the best approach to deal with JavaScript’s dynamic nature.
A Java transpiler, for instance, takes the Java code and transpiles it to JavaScript at compile time. So we have all the advantages of a statically typed language, and its tooling, targeting the browser.
Java-to-JavaScript Transpilation
The transpiler that we use in our architecture, is GWT. This choice is a bit controversial, especially because the GWT framework was launched in 2006 when the web was a very different place.
But keep in mind that every piece of technology has its own good and bad parts. For sure there are some bad parts in GWT (like the Swing Style Widgets, multiple permutations per browser/language), but keep in mind that for our architecture what we are trying to achieve is static typing on the web, and for this purpose the GWT compiler is amazing.
Our group is part of the GWT steering committee, and the next generation of GWT is all about JUST these good parts. Removing or decoupling the early 2000 legacy and keeping only the good parts. In our opinion the best parts of GWT are:
- Java to JavaScript transpiler: extreme JavaScript performance due to compiling optimizations and static typing in the web.
- java.* emulation: excellent emulation of the main java libraries, providing runtime behavior/consistency.
- JS Interop: almost transparent interoperability between Java <-> JavaScript. This is a key aspect of the next generation of GWT and the Drools/jBPM platform: embrace and interop (two-way) with JS ecosystem.
Google is working on a new transpiler called J2CL (short for Java-to-Closure, using the Google Closure Compiler) that will be the compiler used in GWT 3, the next major GWT release. The J2CL transpiler has a different architecture and scope, allowing it to overcome many of the disadvantages of the previous GWT 2 compiler.
Whereas the GWT 2 compiler must load the entire AST of all sources (including dependencies), J2CL is not a monolithic compiler. Much like javac, it is able to individually compile source files, using class files to resolve external dependencies, leaving the greater potential for incremental compilation.
These three good parts are great and in our opinion, you should really consider using GWT as a transpiler in your web applications. But keep in mind that the most important point here is that GWT is just our first pillar of implementation. You can consider using other transpilers like Typescript, Dart, Elm, ScalaJS, PureScript, or TeaVM.
The key point is that every web application should be handled as a large-scale application, and every large-scale application should be concerned about effective refactoring. The best way to achieve this is using statically typed languages.
This is the first of three posts about our 5 pillars of successful web applications. Stay tuned for parts 2 and 3 to follow.
[I would like to thank Max Barkley and Alexandre Porcelli for kindly reviewing this article before publication, contributing with the final text, and providing great feedback.]
To build your Java EE Microservice visit WildFly Swarm and download the cheat sheet.
Last updated: January 22, 2024