Over the last few weeks, I've seen many people posting about Hexagonal Architecture, which sparked my curiosity. I decided to read up on it and even consulted AI to understand it better. Here's a summary of what I've learned!
Hexagonal Architecture, also known as Ports and Adapters Architecture, has a primary goal: to isolate the main business logic (the "inside" or "core") from external concerns like databases, user interfaces, or third-party services (the "outside"). This concept might sound familiar if you know about the principle of separation of concerns. Indeed, the aim is to decouple the core logic from these external dependencies.
So, how does the outside logic communicate with the inside logic? The answer lies in its other name: Ports and Adapters. The inside logic provides ports, which are essentially interfaces. These interfaces are then implemented by adapters in the outside logic. Let’s discuss ports and adapters in more detail.
What are Ports?
Imagine your laptop is the inside (core) logic, and it has several ports (like USB, HDMI, etc.). Your laptop itself doesn't know what specific devices will be plugged into these ports—it could be an external mouse, a keyboard, a monitor, or something else. However, your laptop provides a set of rules or protocols for these ports. Whatever you plug in must follow these protocols to communicate correctly.
In software terms, a port is like an interface defined by the core logic. This interface acts as a contract, ensuring that any external component (adapter) interacting with the core through this port adheres to the defined methods and data structures.
What are Adapters?
As their name suggests, adapters are like translators or connectors. They are the components in the outside logic that implement the ports (interfaces) provided by the core. This allows external systems or components, which might have different technologies or APIs, to communicate with the core logic in a standardized way.
There are two main types of adapters:
- Primary Adapters (Driving Adapters): These adapters drive the application. They are the ones that initiate interaction with the core logic. Think of a user interface, a REST API controller, or automated tests. They take user input or external requests and send commands to the application's core through its ports.
-
Secondary Adapters (Driven Adapters): These adapters are driven by the application. The core logic uses these adapters to interact with external systems or tools, such as databases, message queues, or third-party APIs. The core defines a port (an interface like
SaveUserRepository
), and the secondary adapter (e.g.,PostgresUserRepositoryAdapter
) implements this interface to perform the actual saving.
We can think of these as inbound (primary) and outbound (secondary) interactions.
Revisiting the Laptop Analogy
Let's use the laptop analogy again. Imagine you open a note-taking application on your laptop.
Your 'inside' (the application's core logic) has a protocol (a port) for actions like "type a sentence" or "move cursor."
Now, you can plug in an external keyboard or mouse. These physical tools are like primary adapters. As long as they "implement" the laptop's input protocols (e.g., USB HID standards), you can use them to type and move the cursor. The keyboard sends signals to the laptop's core.
After you finish typing your note, you want to save it. Your 'inside' (the application's core logic) also has a protocol (another port) for "save data."
This saving operation can then be handled by a secondary adapter. For instance, you could plug in an external HDD or SSD to save the file locally, or the application might use an adapter to upload it to cloud storage. In this case, the laptop's core initiates the action of saving through the adapter to an external system.
What Makes It Great?
As mentioned earlier, the great thing about this architecture is that we can maintain and modify multiple external components (adapters) without needing to alter the core business logic.
- Independence: The core logic remains independent of external technologies or changes in those technologies.
- Flexibility: New adapters can be added to support new databases, UI frameworks, or external services without touching the core.
- Testability: The core business logic can be tested in isolation, without needing real databases or UIs, by using mock adapters.
- Maintainability: It can help prevent major bugs originating from the main business logic from affecting unrelated parts and can make it easier to trace and monitor issues by localizing them to specific adapters.
When to Be Cautious
Despite these benefits, there's no one-size-fits-all solution in software architecture.
Hexagonal Architecture might be too complicated for straightforward and simple systems. Some applications have simple enough functionality that it would be faster to develop them using a more traditional layered architecture rather than strictly separating the core logic from all external concerns.
This architecture tends to be more suitable for systems with significant or complex domain logic that has unique calculations, rules, or methods that you want to protect and evolve independently of infrastructure concerns.
Top comments (0)