I’m sure you are interested in microservices. Microservices are one of the most promising new application architectures. By separating a single application into many loosely coupled and independently deployable smaller services, microservices make applications easier to scale and deploy.
When we deploy microservices, we’re faced with the problem of the best way to package them. I’ll take a deeper look at microservices in a future post. But first, let’s think about how we package a Java application.
The standard way to package a Java application is to create a JAR or WAR file containing the components we need. The acronym JAR stands for Java Application aRchive, while WAR means a Web Application aRchive.
Types of Java application packages
JAR and WAR files come in different types, depending on which of our Java application components we package and how we package them.
We have a choice between fat, skinny, thin and hollow JARs and WARs.
From smallest to biggest, they are:
-
Skinny — Contains ONLY the code we wrote, and nothing else.
-
Thin — Contains our code PLUS the application’s direct dependencies (utility libraries, database drivers, etc.).
-
Hollow — Contains only what’s needed to run our application, but does NOT include our code. A hollow JAR is like a pre-packaged application server into which we will later deploy our application. This is conceptually similar to how standard Java/Jakarta EE application servers work. Hollow JARs work well for containers like Docker. Hollow is the opposite of thin.
-
Fat/Uber — Contains the code that we wrote PLUS the application dependencies PLUS everything else needed to run our code. Maven and Spring Boot have used this packaging approach. This makes the application easy to deploy and run on a standard Java runtime environment (JRE) directly from the command line using
java -jar myapp.jar
. The downside is that the build process is slower and inefficient, and the JAR files are huge.
Choosing your Java application package
Fat/Uber JARs are appealing because they are portable, self-contained, easy to distribute and only need a JRE to run. The downside is they can get huge and their startup times are slower.
The more we can separate the layers into skinny or thin JARs, the more we will save on resources (network/disk/CPU) during development and deployment. We will also then have the benefit of being able to independently patch any of the layers to deploy fixes to all our running apps.
Skinny JARs are at the opposite end of the spectrum to fat JARs. They are very small, very quick to deploy, and use very few resources. However, we need a running application container before we can deploy, and it’s harder to test the application. If we deploy hundreds or thousands of microservices, the benefit of using skinny JARs far exceeds the initial pain of setting up the deployment environment.
Both fat JARs and thin/skinny JARs are viable alternatives. The choice of which to use should always be the one best suited for the job. At the end of the day, it’s up to each team to decide on the trade-offs depending on their CI/CD (continuous integration and continuous delivery) strategy, turnaround times and number of deployed microservices.
In this article I’ve summarised the most important introductory parts from two really good articles, one from DZone and the other from Redhat.
Was this useful? Please share your comments and questions