Hexagonal Architecture
A term coined by Alister Cockburn, technology evangelist and one of the guys who signed the Agile manifesto, defines that Hexagonal Architecture has the purpose to decrease technical debt while it increases software maintainability to turn faster and easier feature implementation as the software complexity grows.
This architecture imposes a certain level of discipline and bureaucracy in software development. It does that intending to assure code maintainability in the long run through an architecture that aims to achieve loosely coupling between which is referred to as business and technology within a software.
The hexagonal architecture discipline is a smaller price to be paid if compared with the price paid for increased technical debts generated by the lack of discipline in software architecture.
As technical debt increases so harder to maintain software it becomes.
The hexagonal shape comes from the idea that communication with a system can be done through different technologies and protocols as such HTTP via browser or REST API, SQL, MQ, FTP, SMTP, CICS, etc. This communication occurs at input and output adapters localized in the hexagonal extremities and allows users and other applications to send and receive data from the system.
To allow a hexagonal architecture based application to be accessed by others through, for example, the HTTP protocol, it's necessary to define an Input Adapter (Framework) for HTTP communication. This adapter handles an Input Port (Application) that implements an Use Case (Application) in charge to execute Specific Application Rules that enable execution of Critical Business Rules (Domain).
In the same way, if a hexagonal architecture application must communicate with a SQL database, so it's required to define an Output Port (Application) that will be an interface implemented by an Output Adapter (Framework). The usage of interfaces by Output Ports are abstractions to assure loosely coupling between business rules and technical details needed to allow execution of business rules. Observe, however, a communication that starts outside the hexagon (Driver Side) and the one starting from within the hexagon (Driven Side). We call driver all communications starting outside and driven the ones starting within the hexagon.
Around the hexagon application, we can have databases, queues, other hexagon applications, email servers, AD servers, and other things communicating with the hexagon. So all the data transfer begins with either Input and Output Adapters who start a flow that will pass through Framework, Application and Domain hexagons.
Input Adapters
In the context of driver operations, input adapters are classes that expose protocols through which it's possible to communicate with a system. Considering a Java app scenario, an input adapter could be a class exposing via HTTP an endpoint to allow REST or SOAP communication.
It occurs from outside to inside the application (driver). For example, Javascript frontend talking to a Java backend.
Input adapters are within the Framework hexagon.
Output Adapters
In the context of driven operations, output adapters are interface implementations whose contract is defined by output ports. Hence, it's possible to create different output adapters since they all respect the contract defined by an output port. For example, a persistence port, it is technology agnostic, it tells only which kind of data should be persisted/obtained to meet business rules requirements. The output adaptor for this port could be a class implementing a method responsible to create connections to either a SQL Oracle database or a NoSQL MongoDB.
It occurs from inside to outside the application (driven). For instance, a backend Java persisting data in MongoDB.
Output adapters are within Framework hexagon.
Business Rules
It refers to procedures required for a company to earn or save money. In old telecom companies, in the 70s for example, phone line activations were performed manually procedures. After the execution of these procedures was completed so the company could charge money from customers. The only difference today is that all those procedures are now made automatically by software.
Entities
Entity is an object composed by two things:
- Critical Business Data
- Critical Business Rules
So it should not exist within an entity things related to database, UI, frameworks, etc. The only thing allowed in an entity is data related to business rules and nothing more.
Entities are within Domain hexagon.
Use Cases
In the previous example about business rules, it was talked about the use of automation to execute critical business rules with software.
There exist procedures that arise just because of automation via software, there should not be so much sense in the use of these procedures in a context where all things are done manually.
For instance, imagine an application showing which internet plans are available to a certain region. One requirement for internet plans screen to appear is ZIP code validation. That's it, we have a use case.
So use cases are a description of how an automatized system is utilized. It specifies:
a) Input data a user should inform
b) Output data to be returned
c) Processing steps to produce the output
Contrary to Critical Business Rules that exist within an Entity, with Use Cases we have Application-Specific Business Rules. These rules refer to the automation process of activities performed by Critical Business Rules.
Use Cases are within Application Hexagon.
Queries
They are a mechanism utilized when there is no need to change or execute something related to domain entities. When there is a need to, for instance, retrieve data through one of the application adapters, be it a database, HTTP or LDAP adapter.
Queries are within Application Hexagon.
Commands
In a context of a Java application, commands can be nested classes inside a use case implementation that serves as a means to map data that comes outside the hexagon. It's the mechanism that translate data passing through different hexagons.
Commands are within Application Hexagon.
Input Ports
Input ports are classes implementing interfaces defined by use cases. A use case, as already seen, defines Application-Specific Business Rules. It does that with abstractions created by interfaces and abstract classes.
The main advantage of this is approach is Dependency Inversion and the application of OCP (Open-Closed Principle) because in this way it's possible to achieve loosely coupling and, if necessary, create several input ports for the same use case.
Input ports have a role in the context of driver operations, that is, it occurs from outside to inside the application.
Input ports are within Application Hexagon.
Output Ports
They have different behavior because their roles are related to driven operations. Output Ports are interfaces defining which kind of operations are possible to execute in the outside direction of a hexagonal application. For instance, data persistence or integration with other applications.
Output ports are implemented by output adapters. So here is possible to observe dependency inversion because output adapters depend on output ports that depend on an entity.
Output ports are within Application Hexagon.
Domain
All Critical Business Rules, so all entities, should be within Domain hexagon. The software development should start from the Domain, where it'll define all entities along with critical business operations required by the system. The main advantage of such approach is the possibility to start development and postpone decisions regarding which kind of technology should be used, this idea is presented by Robert C. Martin in his book Clean Architecture.
Classes from Domain should not depend on anything that exists in other hexagons. By doing that it's possible to achieve dependency inversion and strong loosely coupling. Code concerning technology should reside in Application and Framework hexagons and they must depend on Domain whereas critical business and stable code should reside in Domain hexagon and must not depend on code from other hexagons.
Package Structure
Here is a package structure example of a hexagonal architecture based application:
Application
It's here we should put Input and Output Ports, Use Cases, Commands and Queries.
Framework
Input and Output Adapters should be put in this package.
Domain
All entities should be put here. This is where we'll be able to find all Critical Business Rules.
The code example for a hexagonal architecture application is available on Github.