Dependency management of Java EE projects

The dependency management of an enterprise project targets the dependencies that are added on top of the JDK. This includes dependencies that are required during compilation, tests, and at runtime. In a Java enterprise project, the Java EE API is required with provided dependency scope. Since the APIs are available on the application server, they don't have to be included in the packaged archive. The provided Java EE API therefore doesn't have an implication on the package size.

Real-world enterprise projects usually include more dependencies than this. Typical examples for third-party dependencies include logging frameworks such as Slf4j, Log4j, or Logback, JSON mapping frameworks such as Jackson, or general purpose libraries such as Apache Commons. There are several issues with these dependencies.

First of all, third-party dependencies are usually not provided, thus increasing the size of the artifact. This doesn't sound that harmful, but has some implications that we'll see later. The more dependencies are added to the resulting artifact, the longer the build will take. Build systems need to copy potentially big dependencies into the artifact each and every time the project is built. As we will see in Chapter 6, Application Development Workflows, project builds need to be as fast as possible. Every dependency added to the package increases the turnaround time.

Potential collisions of dependencies and their versions represent an even bigger challenge. This includes both packaged dependencies and transitive dependencies as well as libraries that already exist on the application server. For example, logging frameworks are often already present in the container's classpath, potentially in a different version. Different versions being used introduce potential issues with the aggregate of libraries being there. Experience shows that implicit dependencies that are added transitively represent the biggest challenge in this regard.

Aside from technical reasons, there are some other aspects to consider before lightheadedly introducing dependencies to a software project. Dependency licenses, for example, can become an issue when developing a software product that is shipped to customers. It's not only required that the company is permitted to use certain dependencies, but also that involved licenses are compatible to each other, when shipped in a software package. The simplest way to meet licensing criteria is to avoid dependencies, at least, if they serve no business purpose. Engineers should make similar consideration in regard to security, especially for software being developed for sectors with high demands in security.

I was once involved in a firefighter job responsible for updating versions of used frameworks in an enterprise project. The project included a lot of build dependencies. With all its included third-party dependencies, the project runtime eventually contained all known logging frameworks. The same was true for JSON mapping frameworks, which introduced a lot of version conflicts and runtime dependency mismatches. This was before the advent of JSON-B and JSON-P. We spent most of the time configuring the project build, untangling and excluding the transitive dependencies from the project artifact. This is a typical issue when using third-party libraries. The price for saving project code is to spend time and effort configuring the project build and potentially untangling dependencies, especially if they introduce a lot of transitive functionality.

By managing build dependencies, engineers focus on aspects that are insignificant to the business use cases. The question to be asked is whether it pays off to save some lines of code, when at the same time we introduce dependencies. Experience shows that the trade-off of duplication versus lightweightness, such as in dependency-free projects, is too often in favor of avoiding duplication. A prime example for this are projects that introduce the whole Apache Commons library to use a functionality that could have been realized with a few lines of code.

Whereas it's good practice to not reinvent the wheel by developing own versions of functionality that could be reused, the consequences also have to be considered. Experience shows that introduced dependencies are quite often neglected and only utilized marginally. Most of them serve little business value.

When engineers inspect code quality, for example using code analysis tools, what also should be considered is the ratio of dependencies and project code that target business use cases versus plumbing. There is a straightforward method that can be applied for dependencies. Before a third-party dependency is introduced, consider a few questions. Does adding the functionality add value to the business? How much project code does it save? How big is the impact on the resulting artifact?

For example, imagine part of the use case of the car manufacture application is to communicate with specific factory software using a proprietary Java API. Obviously, the communication is crucial to fulfill the business purpose and it makes a lot of sense to include this dependency in the project. On the contrary, adding a different logging framework hardly improves the application's business value. Furthermore, Chapter 9, Monitoring, Performance, and Logging will discuss the issues with traditional logging.

However, in order not to unnecessarily increase the build size, crucial dependencies can be installed on the application server and be declared as provided in the project's build.

In the first chapter, we saw the difficulties with shared business dependencies such as shared models. Ideally, applications are as self-sufficient as possible. Chapter 8, Microservices and System Architecture will deep-dive into self-contained systems and the motivation for architectures that share nothing.

In regard to technical dependencies, however, the Java EE API already includes the technology that the majority of enterprise applications need. Ideally, engineers develop zero-dependency Java EE applications that are packaged as thin deployment artifacts containing the application-relevant classes only. If some use cases require third-party dependencies, they can be installed in the container. The goal is to have a light footprint of deployment artifacts.

For production code, this means that only provided dependencies are included, ideally, only the Java EE API. Test dependencies, however, are a different story; software tests require some additional technology. Chapter 7, Testing covers the required dependencies for the test scope.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.226.185.87