The systems we brought into the world talked with the operating system using POSIX commands, with each other by sending messages over pipes, tcp channels and finally using the HTTP as the universal communication layer.
The world of modern application development is a world shaped by two opposing communication forces:
- our systems need to talk and have a meaningful relationship with humans
- our systems need to talk and have a meaningful relationship with each other.
What lays in the between is the void we call our software, our business layer, our domains. Our work.
Why do spend so much time talking, writing, ranting, discussing, arguing about everything elese except how to best to build our main deliverable, the core of our application systems?
The reason is simple. Communication is hard. Communication is the biggest problem we humans have not yet successfully solved.
As human beings we are tough the basics of communication, the rules governing our interactions, the principles of speaking and writing and scant else. We are left to our divise to learn the best way to communicate with each other in a variety of different context.
Software is built to simplify human systems. To facilitate interactions, processes and in the end to ease the way we interact with each other.
A modern application is not longer a calculating, mathematical system. Its a living, breathing communication entity which needs to properly interact with humans and other digital systems. It needs to be able to talk, to understand and communicate.
How we built our systems is all centered how those systems need to communicate with all other interested parties. The main architectural and development styles are all centered how to most effectively connect our main working deliverable , our logical part, with the outside world.
Nowadays its more easier to find content related how to build UI systems, application services or persistent storage communication than to find content related how to build the core of our system.
We all know to how develop and how to solve problems.
We do not know the perfect way to communicate.
As an industry we are always trying to change the state of the art of how we develop our software. In the mid twothousands we tried to escape spaghetti code applications where the application logic, external communication points (e.g. what our applications are exposing to the outside - UI, web services etc) and our internal communication points (database, web services we consume or other stuff our logical part needs to work perfectly) and thus we focused on the layer application architecture, the MVC design pattern.
A typical application architecture from the beginning of the millennium looked like this:
I present you the archetype layered application architecture. Our presentation layer communicated with our logical/domain layer who in turn used the Data access layer. On the sides we had our framework and other cross cutting aspects which pervasively transcended our layers.
It was a good model and a good starting point for all our applications then and nowadays. The problem was that it didn't work for all the scenarios we tried to do and in the end it was broken since we couldn't use this architecture to solve our problems. Thus the architecture of our solutions started to dilute, to be broken because it was really hard to stick to this process. We knew it didn't make sense for what we tried to do.
Alistair Cockburn came with the Hexagonal architectural model (or the "Ports and adapters") to better describe the architecture best suited for modern application (systems) development. The general idea is more aligned with hour our system ended out to be when have broken out of the layered application design.
In the hexagonal architectural model a single solution is composed of the following component types:
- One pure logical model
- One or more external communication points (ports)
- Zero or more internal communication point (ports)
- One or more integration environments providing adapters for one or more external communication points and all internal communication points
The pure logical model is the working deliverable our system produces. It has the minimal amount (ideally zero) direct outside system dependencies needed in order to properly function.
External communication points (ports) are the interfaces, the services, the information offering it provided to the world at large. In order to interact with the pure logical model one must go trough them. A pure logical model needs at least one external communication point, but it should provide more points grouped into different context based on the type of interaction and specif usage requirements of the outside components making use of the pure logical model.
Since external communication points are bundled with the logical model they are allowed to call and make use of each other , or better yet make use of a shared internal external communication point generalizing common outside communication patterns.
Internal communication points (ports) are required by the pure logical model in order for it to successfully complete its work. They are not required, it is possible that the pure logical model does not require services provided by other logical models, third party systems or persistent storage points.
Internal communication points can called only from the logical model or by each other.
The combination of a pure logical model, its external and internal communication points is the first unit of reusability - a self contained unit.
In order to complete our system we need an integration environment. The integration environment is responsible for taking one or more self contained units and :
- exposing one or more external points (ports) to different clients trough its adapters
- providing adapters for all internal communication points.
A single self contained unit can be exposed trough different integration points, the specifics of each integration point are not longer a core architectural choice but a plugin, something which can be changed and replaced. Thus the core deliverable of a hexagonal architecture is a specific integration enviroment serving a specific scenario or case.
The critical challenge related to the architecture of hexagonal solutions is the choice of integration patterns. And that is something we have been doing with our applications for quite some time. By applying the same principles used to build layered applications we can quite easily and more naturally build haxagonl solutions.
A specific technical example for a natural hexagonal architectural solutions is the Node.js Express web framework. Express is a very simplistic solution. In essence it provides only two things:
- Routing http requests
- Ability to work with Http Request and Responses on a high level
everything else is handled by introducing components called middleware, which include everything from error utilities, json serializes and deserializes, logging and our application code.