Search icon
Arrow left icon
All Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletters
Free Learning
Arrow right icon
Designing Hexagonal Architecture with Java

You're reading from  Designing Hexagonal Architecture with Java

Product type Book
Published in Jan 2022
Publisher Packt
ISBN-13 9781801816489
Pages 460 pages
Edition 1st Edition
Languages
Author (1):
Davi Vieira Davi Vieira
Profile icon Davi Vieira

Table of Contents (21) Chapters

Preface Section 1: Architecture Fundamentals
Chapter 1: Why Hexagonal Architecture? Chapter 2: Wrapping Business Rules inside Domain Hexagon Chapter 3: Handling Behavior with Ports and Use Cases Chapter 4: Creating Adapters to Interact with the Outside World Chapter 5: Exploring the Nature of Driving and Driven Operations Section 2: Using Hexagons to Create a Solid Foundation
Chapter 6: Building the Domain Hexagon Chapter 7: Building the Application Hexagon Chapter 8: Building the Framework Hexagon Chapter 9: Applying Dependency Inversion with Java Modules Section 3: Becoming Cloud-Native
Chapter 10: Adding Quarkus to a Modularized Hexagonal Application Chapter 11: Leveraging CDI Beans to Manage Ports and Use Cases Chapter 12: Using RESTEasy Reactive to Implement Input Adapters Chapter 13: Persisting Data with Output Adapters and Hibernate Reactive Chapter 14: Setting Up Dockerfile and Kubernetes Objects for Cloud Deployment Chapter 15: Good Design Practices for Your Hexagonal Application Assessments Other Books You May Enjoy

Chapter 9: Applying Dependency Inversion with Java Modules

In the previous chapters, we learned how to develop each hexagon as a Java module. By doing that, we started to enforce the scope and responsibilities of each hexagon in the architecture. But we did not go too far in exploiting the Java module's features, such as encapsulation and dependency inversion, and how these features can enhance the overall structure of a hexagonal system by making it more robust and loosely coupled.

To understand the role that's played by the Java Platform Module System (JPMS) in developing a hexagonal system, we need to understand what problems JPMS aims to solve. Once we know what we can do with JPMS in terms of encapsulation and dependency inversion, we can apply these techniques in conjunction with the hexagonal architecture.

So, in this chapter, we will learn how to combine JPMS with the hexagonal architecture to create a well-encapsulated system with clearly defined boundaries...

Technical requirements

To compile and run the code examples presented in this chapter, you will need the latest Java SE Development Kit and Maven 3.6 installed on your computer. They are all available for the Linux, Mac, and Windows operating systems.

You can find the code files for this chapter on GitHub at https://github.com/PacktPublishing/Designing-Hexagonal-Architecture-with-Java/tree/main/Chapter09.

Introducing JPMS

Before Java SE 9, the only mechanism we had to handle dependencies in Java was the classpath parameter. The classpath parameter is where we put dependencies in the form of JAR files. However, the problem is that there is no way to determine which JAR file a particular dependency came from. If you have two classes with the same name, in the same package, and present in two different JAR files, one of the JAR files would be loaded first, causing one JAR file to be shadowed by the other.

Shadowing is the term we use to refer to a situation where two or more JAR files that contain the same dependency are put into the classpath parameter, but only one of the JAR files is loaded, shadowing the rest. This JAR dependency entanglement issue is also known as JAR hell. A symptom that indicates that things are not so good with dependencies that have been loaded into the classpath parameter is when we see unexpected ClassNotFoundException exceptions at system runtime.

JPMS...

Inverting dependencies on a hexagonal application

The Dependency Inversion Principle (DIP), as introduced by Robert C. Martin, states that high-level components should not depend on low-level components. Instead, both of them should depend on abstractions. At first glance, for some, it may not be so obvious to understand such a concept. After all, what do high- and low-level components mean? And what kind of abstractions are we talking about?

A high-level component has a set of operations orchestrated to enable a major system behavior. A high-level component may rely on low-level components to provide a major system behavior. A low-level component, in turn, utilizes a specialized behavior that supports the goals of a high-level component. Let's consider a client code that acts as the high-level component because it depends on and consumes the functionalities provided by the low-level component.

The high-level component can be either a concrete or abstract element, while...

Using the Java platform's ServiceLoader class to retrieve JPMS provider implementations

So far, we have configured the module descriptor of the Application and Framework hexagon modules. We have refactored the input adapters so that they only depend on the abstractions provided by use cases interfaces. But how can we retrieve the concrete instances that implement those use cases interfaces? That's exactly what the ServiceLoader class does.

ServiceLoader is not a new class made solely to support JPMS features. Instead, ServiceLoader has been present in Java since version 1.6. From Java 9 onward, this class was enhanced to work with the Java modules' services. It relies on the configuration provided by the module descriptor to find implementations for a given service provider interface.

To illustrate how we can use ServiceLoader, let's update the FrameworkTestData test class by creating a method called loadPortsAndUseCases. This method uses ServiceLoader to...

Summary

In this chapter, we started by looking into the motivations and benefits behind JPMS. We discovered that one of the problems JPMS helps to solve is that of JAR hell, where it's difficult to control the dependencies that an application should expose and use. JPMS addresses this problem by closing access to every public type in a module, requiring the developer to explicitly state which packages containing public types should be visible to other modules. Also, the developer should state the modules that a given module depends on in the module descriptor.

We discussed the DIP and recognized the use cases, input ports, input adapters, and output adapters as components that we can apply to the DIP. Then, we used JPMS features such as consumers, services, and providers to refactor the topology and inventory system to enable dependency inversion in conjunction with hexagonal architecture components.

By employing DIP, we created a more supple design, an important characteristic...

Questions

  1. Which JAR dependency problem does JPMS aim to solve?
  2. Which JPMS directive should we use to enable access to a package containing public types?
  3. To declare a dependency on a module, which JPMS directive should we use?
  4. When applying dependency inversion on the hexagonal architecture, which components can be regarded as high-level, abstraction, and low-level?

Further reading

  • The Dependency Inversion Principle, by Robert C. Martin, C++ Report, 1996.
lock icon The rest of the chapter is locked
You have been reading a chapter from
Designing Hexagonal Architecture with Java
Published in: Jan 2022 Publisher: Packt ISBN-13: 9781801816489
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime}