Since I started to work in software development, I have always felt overwhelmed by the recurring challenge of untangling and learning complex code. For me, part of that complexity derived from tight schedules, speed for speed’s sake, feature quantity over code quality, and management pressure to deliver working software no matter the consequences. Under such circumstances, I always find it hard to find enough time for refactoring and other software quality concerns. And I also considered it unfair to blame people who had to write poorly structured code because they had no other choice.
For those interested in economics, there is an axiom that says we’re always in a state of scarcity. Nothing is unlimited in this world. Certain people may experience abundance, but the harsh reality is that all resources are scarce: water, food, electricity, money. They all are served in a limited amount. Wise, and very often richer, are those who know how to use their resources intelligently to produce good results. This scarcity condition cascades to all human activities. Software development is one of them.
Every software project is given a limited number of resources to be carried out. We call successful the software project that knows how to use its resources intelligently. Working software is the most important output of a successful project. Unfortunately, this is not the only success criteria. If we deliver working software that is hard to maintain and expect to change that software in the future, then we’re not using our resources intelligently. So, in my view, a successful software project delivers working and maintainable software.
But how can we create working and maintainable software with scarce resources? I think this question has existed and has remained partially answered since the first line of code was written. I say partially answered because the circumstances of every software project may be so peculiar that it’s hard to find an answer that encompasses all possibilities. Software architecture is one of the efforts to organize ideas supporting the creation of well-structured and maintainable software.
One notable software architecture effort came from a guy called Alistair Cockburn. He was aware that a software system might contain code that exists only to solve business problems. On the other hand, he noted that there is also code that exists only to allow the software system to integrate with external systems and technologies. He then conceived an approach to organize the code and avoid the problems that arise when we mix code that solves business problems with code that integrates technologies. He gave that approach the name hexagonal architecture.
It’s the goal of this book to explore and extend hexagonal architecture ideas. More than that, we aim to frame hexagonal architecture in the context of a recent trend in the technology industry: cloud-native development. We live in the cloud era. It’s hard to talk about any software development topic and ignore how it relates to the cloud’s contemporary software practices. To help us in such an undertaking, we chose Quarkus as the cloud-native framework to guide us in this fascinating cloud-native development world.
You can expect a hybrid approach where theory and practice are blended to improve the learning experience. We’ll spend some time discussing concepts, but we’ll also have fun by dirtying our hands while putting these concepts into practice.
You may have reached a dead-end in your software project where things are so hard that new features are too complex to add and existent ones are equally difficult to maintain. Maybe you’re an enthusiast searching for new ideas to improve your code base. No matter your reasons, I invite you to be my guest on this enlightening learning journey.
Who this book is for
This book is for software architects and Java developers who want to improve code maintainability and enhance productivity with an architecture that allows changes in technology without compromising business logic, which is precisely what hexagonal architecture does. Intermediate knowledge of the Java programming language and familiarity with Jakarta EE will help you to get the most out of this book.
What this book covers
Chapter 1, Why Hexagonal Architecture?, starts by saying that software that is not well organized and lacks sound architecture 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 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, 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. And finally, this chapter explains how use cases and ports are grouped together 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 brokers or other applications. Finally, it shows how all adapters are grouped together 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 Framework hexagon through an input adapter, then handed down to an input port on the application framework until it reaches Domain Entities on 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 how to create an input adapter and how it will carry its operation through an input port. After that, an output adapter will be 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 terms. Finally, it 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 this 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 has been developed in the previous chapters. It introduces the creation of a fourth module, called bootstrap, that serves to get the application started and is used to group together 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 shows how to implement them on input ports and output ports. Finally, it 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 SwaggerUI. 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 on 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, 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 Domain Driven Design 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. And finally, we discuss the consequences of having to maintain multiple adapters.
Assessments contains all the answers to the questions from all the chapters in this book.
- 关于本书的内容介绍、目录、详情等请在 Amazon、Goolge Books 等售书网站搜索查看，本站仅展示封面作为参考。
- 如无特殊说明，本站提供的所有pdf均为文字版（aka True PDF or Digitally Created PDF）。
扫描下方二维码添加微信号 bookyage 回复本书编号 299529 即可，我们会尽快（一般24小时之内）将本书PDF文件以百度网盘链接的形式发送给您。