In this series of posts, we’ll detail our talk presented at Java One San Francisco 2017: "5 Pillars of a Successful Java Web Application”, where we shared our cumulative experience over the years building the workbench and the web tooling for Drools and jBPM platform. If you didn't read the first ones, take a chance to get in touch with the pillars [link for the first].
4th Pillar: 5~10 year Life-Span
The next pillar is how to make my web application last for more than 5~10 years. What’s the expected lifespan of your backend? Probably you don’t plan to throw it away in 2 years.
However, if you talk with some front-end engineers they answer will be slightly different and surprising. Some people said that you should expect to throw away your front-end code every two years, due to the evolution of JavaScript frameworks. Really? Should I rewrite the business logic of my front-end application every two years?
Making an application last is a well-known architecture challenge. Several architectural models share similar principles, i.e., Hexagonal Architecture by Alistair Cockburn, Onion Architecture by Jeffrey Palermo, Clean Architecture by Robert C. Martin. These principles are:
- Decoupled from Frameworks
- Testable
- Decoupled from UI
- Decoupled from Database
- Independent of external systems
In order to illustrate these principles, look on the following Clean Architecture diagram:
The entities and the uses cases should be should be protected from any external agent. This means that an external modification in a UI should never affect them. The real value of our applications is the business rules and the core logic that should be stable and only changed when a business changes.
The importance of this principle is clear to the backend developers. For instance, on AppFormer, we have a virtual file system API; it defines the contract for I/O operations.
This API is a NIO2 wrapper for I/O operations on the regular file system, or in distributed environments can be switched for a distributed GIT NIO2 backport. In that way, we can move from a simple file store to a distributed git backend without having to change any use case. How? Because we protected our use cases with a Virtual File System interface.
For backend developers, this is not new. It's how we currently implement most of our backend architectures.
However, as an industry, when we are architecting a frontend application do we have similar care? Or we just evaluate and adopt web frameworks and follow his model? Aren’t we relying on JS frameworks to define our web architectures?
The problem with that approach is that we need to decide the JS framework on the first day of the project. But the reality is that this will have to last to support business as long as business needs.
But there is a caveat on this approach. There is a good chance that between the 2nd and 4th year of the current JS framework, the project will be canceled or will be replaced by a new version incompatible with the current one. Similar to the Angular 1.x and 2 stories.
So what do we do? Do we rewrite the entire client code? Why do we think it's ok to do this? And what is the risk of remaining in the old version? And the updates? How can I found resources to work with outdated tech? What about security risks? Data breaches for instance.
Robert C. Martin said that a good architecture allows volatile decisions to be easily changed. What if we dealt with the volatility of JS frameworks as a fact?
Our implementation for this problem is part of the Appformer project that Alex Porcelli and I are the leaders.
AppFormer (previously know as Uberfire) is the web-based workbench framework behind Red Hat JBoss Business Rules Management System (BRMS) and Red Hat JBoss BPM Suite (BPMS). AppFormer is also the basis for the next line of business of BRMS and BPMS platform: a low code/no code platform to develop modern business applications. Our initiative aims to allow business users easily build applications by mashing up components and connect them to other Red Hat modules and software.
Our main architectural goal is that nothing in our core business depends on any web framework. And how did we do this?
We have created a programming model that has a well-defined component model based on Screen, Editors, Perspectives, and Popups. Each of these components has also a well-defined life-cycle.
In the user perspective, a Screen is a component. An editor is a component that is associated with a file type. A Perspective is a page.
This is not new; it's the old contract-based architecture. Each component is, in the end, a Java interface. And what are the advantages of this approach?
Do you remember that Errai has a Bean Manager in the browser? A well-defined programming model allows us to quickly switch between implementations of the component interface, instead of coupling with a specific web technology. We render the components based on the interface type and the bean name, not on the real implementation/framework.
We have legacy code, and such code was developed using old technologies, like GWT widgets, but when we modernize such implementations using pure HTML/CSS it's just a matter of switch the implementations because both implementations respect the same contract. That is the beauty of having a contract based model and CDI in the browser.
That is the how we managed to transparently run a GWT code from 7 years ago that implements the Screen interface, along with fresh code implemented in Errai UI.
So, this concludes the 4th pillars of successful web applications. You should prepare your architecture to live more than 2~4 years old life cycle of JS frameworks, and in order to do that, your web architecture should be treated with the same respect as you treat your backend.
5th Pillar: Interoperability
And the last, but not least, the 5th Pillar of successful web applications is interoperability. You need to be flexible in order to stay modern. As we discussed in the 4th pillar, we need to have a solid architecture, but also we cannot be stuck in old tech and need to offer interoperability to my third parties. We need to embrace the new technologies. How can we do that?
We saw in the last pillar, that we use Java interfaces to define our components. In order to avoid boilerplate code, we use Java Annotations Processors to automatically generate adapters from user client to our interfaces. (i.e. @WorkbenchPerspectives annotations trigger the generation for implementation of PerspectiveActivity interface).
To integrate with any external web framework, a new adapter that maps to the target interface and register needs to be implemented. Its implementation takes place in the Errai Bean Manager. After this, everything will become an implementation of the same contract and transparent for the use cases. Here is an example of how we already do this with Angular code.
In this post series, we've presented the 5 pillars that we believe that are the foundation of every successful web application. Each of them has already proved its value in practice in the development of Drools and jBPM workbenches.
Web applications are important pieces of our architecture. Although your application may not implement all these 5 pillars, you should keep them always in mind, because we really believe that all those 5 pillars provide a solid foundation for any web application.
[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.]