This book is suited for Java architects and Java developers at senior and intermediate levels. The reader is expected to have previous knowledge of object-oriented language principles and be acquainted with the Java programming language. Previous professional Java experience is also recommended, as this book is concerned with problems that usually happen in enterprise software development projects using Java.
Table Of Contents
Preface
Sometimes, solving problems and expressing our ideas through code is challenging. What looks like an obvious solution in our minds may look unnecessarily complex in the minds of others. But that’s alright if we are open to new ideas and perspectives because we set our minds to learning unexpected things when we have a persistent attitude and a willingness to embrace everything that comes. I was not expecting it when I was first introduced to hexagonal architecture.
I remember working in a company where most of the software was developed by consultants. Those folks would come, deliver their code, and leave. Although my team and I tried to establish specific standards to ensure consistency in the applications we were responsible for, the harsh reality was that we needed more knowledge to maintain our applications properly. Given the number of systems and the different approaches and architectures employed to build them, it took a lot of work to maintain and add new features to such systems. That’s when a teammate told me about the hexagonal architecture and how it could help us tackle hard-to-maintain software problems.
At that time, there weren’t many books covering the hexagonal architecture. Most of the resources were scattered on the internet through video courses and articles explaining how someone implemented hexagonal architecture. The lack of resources was a considerable obstacle, but using an architecture that could improve software maintainability was very attractive to me. So, I kept researching and experimenting in my own job with the ideas, which would ultimately lead to me writing the first edition of this book.
I am fortunate to have had the opportunity to write the first, and now the second, edition of a book dealing with such a fascinating topic as hexagonal architecture. The second edition has allowed me to employ hexagonal architecture ideas with more recent versions of Java and Quarkus. This edition preserves the fundamentals from the previous one while exploring modern Java’s new and cool features. Also, this edition explores how hexagonal architecture can be used with the acclaimed SOLID principles and how it relates to the widely used layered architecture.
Concepts such as ports, adapters, and use cases are combined with Domain-Driven Design (DDD) elements such as entities and value objects to provide an in-depth guide explaining how to assemble those concepts to untangle the exciting puzzle of designing highly change-tolerable applications with hexagonal architecture. Considering the contemporary cloud-native practices that dictate most enterprise development today, we deep dive into Quarkus to learn how to blend hexagonal architecture ideas with cloud-native development, which enables us to create enterprise-grade hexagonal applications ready to be deployed in any major cloud provider.
So, I encourage you to have a persistent attitude and a willingness to embrace everything that comes and embark with me on this fascinating journey to explore hexagonal architecture.
What this book covers
Chapter 1, Why Hexagonal Architecture?, starts by discussing how software that is not well organized and lacks sound architectural principles may work fine but will present a high risk of developing technical debt. As new features are added, the software tends to become more complex to maintain because there is no common ground to guide the addition or change of features. Based on this problem, this chapter explains why hexagonal architecture helps tackle technical debt by establishing an approach where business code is decoupled from technology code, allowing the former to evolve without dependency on the latter.
Chapter 2, Wrapping Business Rules inside the Domain Hexagon, follows a domain-driven approach and describes what domain entities are, what role they play within hexagonal architecture, and how they wrap business rules and data in simple Java POJOs. It explains why domain entities are the most important part of code and why they should not depend on anything other than other domain entities. Finally, it explains how business rules inside a domain entity can be implemented using the Specification design pattern.
Chapter 3, Handling Behavior with Ports and Uses Cases, covers what use cases are, explaining that they are used to define software intent with interfaces describing things the software can do. Then, it explains what input ports are and the classes that implement use case interfaces, and specifies in concrete ways how the software intent should be accomplished. It talks about output ports and their role in abstractly defining the behavior of operations that need to get data from outside the software. Finally, this chapter explains how use cases and ports are grouped in what’s called the Application hexagon.
Chapter 4, Creating Adapters to Interact with the Outside World, shows how adapters allow the software to integrate with different technologies. It explains that the same port can have multiple adapters. Input adapters, bound to input ports, enable the application to expose its functionalities through different communication protocols, such as REST, gRPC, or WebSocket. Output adapters, bound to output ports, allow the application to communicate with varying data sources, whether it be databases or even message queues or other applications. Finally, the chapter shows how all adapters are grouped in the Framework hexagon.
Chapter 5, Exploring the Nature of Driving and Driven Operations, explains that driver operations drive the software behavior by starting one of its exposed functions. It details the driver operations life cycle, showing how a request is captured on the Framework hexagon through an input adapter and then handed down to an input port on the Application hexagon until it reaches the entities from the Domain hexagon. It shows that a use case starts driven operations from the Application hexagon when the software needs to get data from outside, going from an output port to an output adapter to fulfill the use case needs.
Chapter 6, Building the Domain Hexagon, shows how to start developing a telco’s network and topology inventory application by first creating the Domain hexagon as a Java module. Then, this chapter shows how business rules and data are mapped to domain entities’ classes and methods. The business rules are arranged in different algorithms with the aim of the Specification design pattern. Finally, it shows how to unit test the Domain hexagon.
Chapter 7, Building the Application Hexagon, starts by adding the Application hexagon as the second Java module on the application. It then explains how to create the use case interface that describes the software’s operations to manage the network and topology inventory. It shows how to implement the use case with an input port, giving a detailed description of how the code should be arranged. It details the creation of an output port interface and its role in obtaining data from external sources. Finally, it explains how to test the Application hexagon.
Chapter 8, Building the Framework Hexagon, starts by adding the Framework hexagon as the third Java module on the application. Then, it teaches you how to create an input adapter and how it will carry out its operations through an input port. After that, an output adapter is created through the implementation of an output port. The output adapter will show how data can be fetched from external sources and converted to be dealt with in Domain hexagon terms. Finally, the chapter explains how to test the Framework hexagon.
Chapter 9, Applying Dependency Inversion with Java Modules, talks a little bit about Java modules, explaining why they are important to enforce the hexagonal architecture principles related to dependency inversion. It explains that Java modules don’t allow cyclic dependencies and because of that, there is no way to make two modules depend on each other at the same time. You will learn how to configure the module descriptor in the hexagonal application.
Chapter 10, Adding Quarkus to a Modularized Hexagonal Application, briefly explains the Quarkus framework and its main features. Then, it advances to show how to add Quarkus to the hexagonal application that was developed in the previous chapters. It introduces the creation of a fourth module, called Bootstrap, which serves to get the application started and is used to group the Domain, Application, and Framework modules.
Chapter 11, Leveraging CDI Beans to Manage Ports and Use Cases, explains how to transform the already developed ports and use cases into CDI beans, leveraging enterprise Java’s power in the hexagonal architecture. It starts by explaining what CDI beans are, then it shows how to implement them on input and output ports. Finally, the chapter describes how to adjust the application framework tests to use Quarkus CDI bean test features.
Chapter 12, Using RESTEasy Reactive to Implement Input Adapters, starts by comparing reactive and imperative approaches for REST endpoints, detailing why the reactive approach performs better. It explains how to implement input adapters with Quarkus RESTEasy Reactive capabilities by explaining how to add the correct annotations and inject the proper dependencies to call input ports. In order to expose the hexagonal application APIs, this chapter explains how to add OpenAPI and Swagger UI. Finally, it shows how to test the reactive input port with Quarkus test tools.
Chapter 13, Persisting Data with Output Adapters and Hibernate Reactive, talks about Hibernate Reactive and how it helps Quarkus to provide reactive capabilities for data persistence. It explains how to create a reactive output adapter to persist data to a MySQL database. Finally, it shows how to test the reactive output adapter with Quarkus test tools.
Chapter 14, Setting Up Dockerfile and Kubernetes Objects for Cloud Deployment, explains how to create a Dockerfile for the hexagonal application based on Quarkus. It explains in detail how to package all the modules and dependencies in one single Docker image. It then shows how to create Kubernetes objects such as Deployment and Service for the hexagonal application and test them in a minikube local Kubernetes cluster.
Chapter 15, Comparing Hexagonal Architecture with Layered Architecture, describes layered architecture and explores how layers handle specific system responsibilities, such as persistence and presentation. We then develop an application using layered architecture principles. Finally, to highlight the differences between layered and hexagonal architecture, we refactor the previously layer-based application into a hexagonal one.
Chapter 16, Using SOLID Principles with Hexagonal Architecture, starts by reviewing SOLID principles and observing how each principle helps to build applications with improved maintainability. Then, it explores how SOLID principles can be applied to a system developed with hexagonal architecture. Finally, it presents some common design patterns that can be used while building a hexagonal system.
Chapter 17, Good Design Practices for Your Hexagonal Application, talks about some good practices you can adopt while creating each hexagon for your application. Starting with the Domain hexagon, we focus on DDD aspects to clarify the business problems the application is supposed to solve. Then, we move on to a discussion about the alternative ways to set up use cases and ports in the Application hexagon. Finally, we discuss the consequences of having to maintain multiple adapters.
Download
(Ebook)[https://hexload.com/o4ccp27lmhjm]
See also
- Software Exorcism: A Handbook for Debugging and Optimizing Legacy Code by Bill Blunden (2003)
- Hormones and the Endocrine System: Textbook of Endocrinology by Bernhard Kleine (2016)
- Genetics: A Conceptual Approach 6e by Benjamin A. Pierce (2017)
- A Functional Approach to Java by Ben Weidig (2022)
- Calculus of variations and optimal control by Amol Sasan (2005)