Hexagonal Architecture FAQ
These questions came from Stéfane Wojewoda at InfoQ in France. They are great questions, so I make the answers available here:
From the interviewer :
Alistair in the Hexagone
(note: “Hexagone” is a French word-play, the reference to the Hexagonal Architecture (discussion: Re: Hexagonal architecture) and also to the shape of France, roughly hexagonal, and hence sometimes referred to as “l’hexagone”)
“With microservices architecture the code designed is sometimes as entangled as it was with layered architecture. The business logic is still mixed up with presentation or formatting, leading to a high complexity and some issues when your clients asked for some evolution. In 2005, Alistair Cockburn came up with the concept of Hexagonal Architecture (discussion: Re: Hexagonal architecture) to tackle this problem. The 20th of june 2017, he was present in Paris, France, to explain the Hexagonal Architecture pattern and to live code a basic implementation with Thomas Pierrain.
InfoQ had the opportunity to ask some questions to Dr Alistair Cockburn about the Hexagonal Architecture and where it comes, the use cases to apply it, the first step to implement a Hexagonal Architecture, the symetry of the pattern and the opportunity to use it with microservices architecture.”
Question 1: Could you describe the concept of Hexagonal Architecture?
The “hexagonal” or ports-and-adapters architecture allows you to create your application to work without either a UI or a database so you can run automated regression-tests against the application, work when the database becomes unavailable, and link applications together without any user involvement.
In 5 steps, roughly speaking, it is done like this:
step 1. Draw a circle (or a hexagon). Put everything that touches the real world on the outside. That includes humans, servers, databases, networks, other computers, and in this case, also the test cases. Put only inside what is important for your business to operate. That is your application.
step 2. Organize all the interactions across the boundary of that hexagon according to why they are interacting with the application. Typically there are three reasons:
- things that drive the application, or cause it to take action (I’ll call these “drivers”),
- things that provide information to the application (I’ll call these “repositories”), and
- things that get notified by the application (I’ll call these “recipients”).
Each of those capture a set of verbs or function calls or interactions, and makes a “port” or “interface”.
step 3. Label those clusters with a verb ending in ”-ing” (present participle) saying what they are for:
- This driver port is for “controlling access to the system”, for “requesting poetry”, for “notifying the system of weather changes”.
- This repository port is for “obtaining information about (whatever)”.
- This recipient port is for “notifying recipients about (whatever)”, or for “controlling the release of (chemical ABC)”.
step 4. Allow any desired technology to operate to those interfaces using some adapter code. The test harness is an adapter that allows test cases to run a driver port; a GUI is an adapter that allows a human to run the same driver port. Perhaps another program or an HTTP adapter will run it. Similarly, on the recipient or repository side, adapters allow different technologies to respond to requests and notification: mock databases, flat files, real databases, and so on.
step 5. Now you can swap out any technology for any other one that has an adapter to meet an interface at a port. This allows you to write tests first, then replace those with either humans or other programs; to replace databases with files or mock databases; and so on.
There are two resulting advantages: The first is that all the business logic is neatly kept in one zone, so it can be tested and modified cleanly. This simplifies both testing and evolution of the app. The second is that you can extend the choice of technologies used at each port over time, without touching the internals of the application or having to create multiple versions that have to be maintained separately.
The Hexagonal Architecture is described in full at http://alistair.cockburn.us/Hexagonal+architecture
Question 2: This concept of Hexagonal Architecture is a kind of design pattern. Where does it come from?
The hexagonal architecture is a double application of the design pattern Configurable Dependency (http://alistair.cockburn.us/Configurable%20dependency), once for the drivers and again for each of the repositories and recipients.
I came up with the pattern after having been hurt repeatedly on projects where we needed either to substitute the technology on the driver side, or on the repository / recipient side, and I knew that we shouldn’t need to be hurt. I knew of the Model-View-Controller design pattern, the Mock-Database design pattern hadn’t yet been documented, but I knew the principles of API design and that it should be practical.
The tip-over to writing the pattern was when I was visiting a colleague who was having trouble extending an application that had 4 ports (as we came to discover). I drew the system with the relevant interfaces and saw the shape of the general solution, so I wrote that up.
Question 3: What are the uses cases where you can apply this pattern?
Every design pattern introduces a bit of complexity. You need to decide when that added complexity is worth it. This is an important discussion, for Hexagonal Architecture and its base pattern, Configurable Dependency.
Applying Configurable Dependency to the driver ports is fairly obvious by today’s standards: we have Model-View-Controller, Model-Presenter, and similar patterns. Many people don’t do any of these, but it is really rare that you won’t want this bit of added complexity. You should Configurable Dependency to your driver ports, at least so that your test cases are clean and they test all the business logic.
Applying Configurable Dependency to the recipients and repositories ports is less common. Applying it allows you to test your system in isolation, recover from certain repository failures, and swap repositories easily. Some people tell me they will never swap out the main database for anything else. Sometimes they are right, sometimes they are wrong :).
Question 4: You facilitated a Meetup to explain Hexagonal Architecture and to show how to implement it. Where did this idea come from?
I was watching on Twitter an increasing number of people saying they had given lectures on Hexagonal Architeture. What crossed my mind was that it might be interesting to give the history of the design pattern, its motivation, alongside someone programming it live, so that everyone in the audience could understand why it is useful, and also see for themselves how simple it can be to implement.
Thomas Pierrain turned out to be the perfect presentation partner – his design thinking is very compatible with mine, and his programming skills are tremendous. Romeu Moura and he set up the evening, the room was standing-room-only, which was very gratifying.
Question 5: With [Thomas Pierrain](), you livecoded a Hexagonal Architecture from scratch. What are the first steps to code a Hexagonal Architecture?
In the video, Thomas illustrates an excellent way to begin. Our application was to retrieve a line of poetry from a repository. Thomas and I discussed the “carpaccio” (http://alistair.cockburn.us/elephant+carpaccio) way of constructing this.
step 1. Hard-code a single response inside the application to avoid having to make a repository. Construct a Configurable Dependency driver driven by a test case that asks for a line of poetry and expects that hard coded answer.
step 2. Construct a Configurable Dependency for a repository that is a loopback or mock database, just a function that returns the hardcoded answer.
You are done! At this point, the Hexagonal Architecture is already implemented. From here on, add more drivers and repositories as you wish.
step 3. Construct a repository that is a file or a database. Use the Configurable Dependency mechanism to pass in the new repository instead of the loopback repository. Use the test driver to test that the new repository gives good results.
step4. Construct a live driver, whether from the web or from a UI. Use the Configurable Dependency on the driver side to connect it. Now your application is live, with both a test driver and a live driver, and your choice of repository, loopack or file or databse.
Question 6: In your first article about Hexagonal Architecture, you explained that there is a symmetry between the “left” and “right” side of the Hexagon. Recently, you seemed to disagree, to state that the relation is asymmetric. Could you explain how you discover this asymmetry?
I was motivated to this design pattern by a desire to recognize a similarity between driver and repository sides that had not been recognized to that moment – that the application should not know the nature of the outside technology. All layered architecture diagrams I had seen to that point showed the users on the left and the databases on the right, or the users on the top and the databases on the bottom, as though the databases were somehow more “fundamental” or “deeper” or “more stable”.
I didn’t want to see them as such.
Fortunately for me, I did not know at that time that the implementations of drivers and repositories have a fundamental difference. I say “fortunately”, because probably if I had seen that asymmetry, I could not have constructed the hexagonal architecture pattern!
Slowly, as I worked with the pattern, I saw that although the valuable symmetry is applying Configurable Dependency to all types of external links (drivers, repositories, recipients), there is an implementation difference in terms of who “knows” of whom, in the code.
- In order to drive the recipients or the repositories, the application must store the reference to the recipient or repository.
- In contrast, the application may never know who or what is driving it. That is a fundamental asymmetry.
This asymmetry happens to correspond to “primary actors” and “supporting actors” in the use case world (see “Structuring use cases with goals (discussion: Re: Structuring use cases with goals)”). That is useful, because it helps connect requirements documents with system architecture, with implementation, with this architectural pattern.
The symmetry is that no part of the application even knows the technology it is connected to, neither the primary or driving actors, nor the secondary or receiving actors. The Configurable Dependency takes care of that.
The asymmetry is in how the Configurable Dependency is implemented. The implementation is different for the primary or driving actors than it is for the supporting or recipient / repository actors.
This is how the Hexagonal Architecture can handle both the symmetry and the asymmetry.
Question 7. Hexagonal Architecture is about accessing the “business domain” with ports and adaptaters. Would you recommend it for implementing microservices architecture?
Interesting question. Having not yet implemented microservices myself, I might defer this question. I would look at the situation in the same way, however, and see what that logic produces as a deduction.
Question 8. The French Domain Driven Design (DDD) and craftsmanship communities are fond of this pattern. In your opinion, why is that so?
You will have to ask those people that question!
I will speculate 🙂 that for the DDD community, applying the Hexagonal Architecture allows them to get rid of an annoying problem, that in many impmentations they see, the external technology keeps showing up inside the domain parts of the application. Applying Hexagonal Architecture allows to stay in pure domain discussions during their design and their code, similarly, is pure domain discussion, as it should be. Thus, in my speculation, the Hexagonal Architecture is a prerequisite to doing Domain Driven Design, it permits the DDD discussion to proceed cleanly.
I will speculate for the craftsmanship community, that they are sensitive to the cost of change and dirtiness of the design and code that comes from fixing dependency relationships deep in the code. The cost of change, even technical debt, is quite high when a driver technology or repository technology is buried deep or is hard-coded. Separating the logic using Configurable Dependency reduces the cost of the change and makes the code easier to understand. Hence, it rates high in the craftsmanship community.
But as the people in those two communities what they think!
Thank you for these interesting questions. I hope others find them useful.