Hexagonal architecture is a model or pattern for designing software applications. The idea behind it is to put inputs and outputs at the edges of your design. Having inputs and outputs at the edge means you can swap out their handlers without changing the core code.
The hexagonal architecture divides the system into loosely-coupled interchangeable components; such as application core, user interface, data repositories, test scripts and other system interfaces etc.
In this approach, the application core that contains the business domain information and it is surrounded by a layer that contains adapters handling bi-directional communication with other components in a generic way.
The Ports acts as a gateway through which communication takes place as an inbound or outbound port. An Inbound port is something like a service interface that exposes the core logic to the outside world. An outbound port is something like a repository interface that facilitates communication from application to persistence system.
The adapters act as an implementation of a port that handles user input and translate it into the language-specific call. It basically encapsulates the logic to interact with outer systems such as message queues, databases, etc. It also transforms the communication between external objects and core. The adaptors are again of two types.
Primary Adapters: It drives the application using the inbound port of an application and also called as Driving adapters. Examples of primary adapters could be WebViews or Rest Controllers.
Secondary Adapters: This is an implementation of an outbound port that is driven by the application and also called as Driven adaptors. Connection with messaging queues, databases, and external API calls are some of the examples of Secondary adapters.
It forms the core of the application. It's objective is to cater to the requests of user interfaces. Based on the request, it runs some custom logic, gets the resources needed to fulfil the request and answers back in an agreed upon response format.
There can be many user interfaces to a backend - mobile apps, web apps, desktop softwares, etc. They will all get their resources from the Business logic layer.
Layers of Hexagonal Architecture
Domain API Layer: This layer is the central layer and does not have any dependency on any other layer. Since primary and secondary adapters should be able to implement the contract, it is a contract for domain layer interaction (ports). This is also called as Dependency Inversion Principle Domain layer (DIP).
Domain Layer (Business Layer): These layers are kept clean without any other dependencies. They have the business logic.
Rest Adapter Layer: The rest adapter layer is also called the Left port adapter. The primary adapter is the layer where the restful services are implemented like GET, POST, PUT, DELETE, etc.
Persistence Adapter Layer: This is the Rest Adapter which is also known as the right port adapter or the secondary adapter. This is the layer where the implementation of Entity framework core is done, which previously implemented a repository design pattern. A DbSet is the repository and DbContext is the Unit of Work (UOW). Bootstrap/Presentation Layer:- This is the final layer of the project. Everything begins here.
Principles of Hexagonal Architecture
The hexagonal architecture is based on three principles and techniques:
Explicitly separate User-Side, Business Logic, and Server-Side
Dependencies are going from User-Side and Server-Side to the Business Logic
We isolate the boundaries by using Ports and Adapters
Principle 1: Separate User-Side, Business Logic and Server-Side
The first principle is to explicitly separate the code into three large formalized areas.
On the left, the User-Side
This is the side through which the user or external programs will interact with the application. It contains the code that allows these interactions. Typically, your user interface code, your HTTP routes for an API, your JSON serializations to programs that consume your application are here.
This is the side where we find the actors who drive the Business Logic.
The Business Logic, in the center
This is the part that we want to isolate from both left and right sides. It contains all the code that concerns and implements business logic. The business vocabulary and the pure business logic, which relates to the concrete problem that solves your application, everything that makes it rich and specific is at the center. Ideally, a domain expert who does not know how to code could read a piece of code in this part and point you to an inconsistency.
On the right, the Server-Side
This is where we’ll find what your application needs, what it drives to work. It contains essential infrastructure details such as the code that interacts with your database, makes calls to the file system, or code that handles HTTP calls to other applications on which you depend for example.
This is the side where we find the actors who are managed by the Business Logic.
The following principles will allow to put into practice this logical separation between User-Side, Business Logic and Server-Side.
Principle 2 : dependencies go inside
This is an essential principle for achieving the objective. We have already begun to see this in the previous principle.
The program can be controlled both by the console and by tests, there is no concept of a console in the Business Logic. The Business Logic does not depend on the User-Side side, it is the User-Side side that depends on the Business Logic. The User-Side (ConsoleAdapter) depends on the notion of poem request, IRequestVerses (which defines a generic « poem request » mechanism on the part of the user).
Similarly, the program can be tested independently of its external systems, the Business Logic does not depend on the Server-Side, it is the opposite. The Server-Side depends on the Business Logic, through the notion of obtaining poems, IObtainPoems. Technically a class on the Server-Side side will inherit the interface defined in the Business Logic and implement it, we will see it in detail below to talk about dependency inversion.
Inside and outside
If we see dependency relationships (<<depends on…>>) as arrows, then this principle defines the central Business Logic as inside, and everything else as outside (see figure). We regularly find these notions of inside and outside when we discuss hexagonal architecture. It can even be the fundamental point to remember and transmit: dependencies go inside.
In other words, everything depends on the Business Logic, the Business Logic does not depend on anything. Alistair Cockburn insists on this demarcation between inside and outside, which is more structuring than the difference between User-Side and Server-Side to solve the initial problem.
Principle 3 : boundaries are isolated with interfaces
To summarize, the user-side code drives the business code through an interface (here IRequestVerses) defined in the business code. And the business code drives the server-side through an interface also defined in the business code (IObtainPoems). These interfaces act as explicit insulators between inside and outside.
A Metaphor: Ports & Adapters
The hexagonal architecture uses the metaphor of ports and adapters to represent the interactions between inside and outside. The image is that the Business Logic defines ports, on which all kinds of adapters can be interchangeably connected if they follow the specification defined by the port.
For example, we can imagine a port of the Business Logic on which we will connect either a hard-coded data source during a unit test, or a real database in an integration test. Just code the corresponding implementations and adapters on the Server-Side, the Business Logic is not impacted by this change.
These interfaces defined by the business code, which isolate and allow interactions with the outside world are the ports of the Ports & Adapters metaphor. Note: as mentioned previously, ports are defined by the business, so they are inside.
On the other hand, Adapters represent the external code make the glue between the port and the rest of the user-side code or server-side code. Here, the adapters are ConsoleAdapter and PoetryLibraryFileAdapter respectively. These adapters are outside.
Benefits of Hexagonal Architecture:
Plug and play: This architecture provides us the ability to add and remove adapters according to our requirements during development. For example, we can interchange the GraphQL adapter with the REST adapter without changing the logic
Testability: We can write test cases for each component effortlessly as it decouples all the layers.
Adaptability/Enhance: We can always add a new way to interact with applications easily.
Sustainability: Maintenance becomes easier as all the third-party libraries can be kept in the infrastructure layer.
Database Independent: We can easily switch the database providers as the database is separated from the data access.
Clean Code: UI can be easily implemented as the business logic is kept away from the presentation layer like React, Angular, or Blazor.
Well Organized: New developers will easily get on board and will have a better understanding of the project as it is well organized.
Drawback of Hexagonal Architecture:
Domain Layer becomes heavy as plenty of logic is implemented in this layer.
The level of complexity will increase dramatically as we are building the application with multiple layers of abstraction.
Having many layers of indirection and isolation usually increases the cost of building and maintaining the application. This cost is more than the benefits of the abstraction.
Most web applications will never require the switching of databases and frameworks. They will only be used through a browser. It is a noble goal to create applications with the potential to swap any implementation. However, most applications won’t need that level of adaptability. Hence, it will be a waste of time to build an application that is so ideal.
Resource: blog.octo.com, ParTech, Wikipedia
The Tech Platform