
In yesterday’s “G is for GUI“, I talked about the benefits of cleanly separating the logical design of the user interface from the implementation details of its representation (e.g., separating the logic of applying for a mortgage from the web page the end user clicks the buttons on).
There’s a wider principle at work here. Separating the core logic of our systems from implementation details like the operating system, the file system, network operations (e.g., web service calls), databases and wotnot is, it turns out, a very good idea.
HTML is one of those implementation details, as are front-end frameworks like React and Flutter. Applying for a mortgage is one concern, divs and spans are entirely another. Remembering what customers have ordered in the past is one concern, storing and retrieving orders from an Oracle database is quite another.
An architectural metaphor that seeks to provide a clear distinction pictures the software’s design as a hexagon. Inside the hexagon is the essential core logic. The sides of the hexagon represents clearly distinct implementation details – separate concerns – like the UI, the file system, databases, user authentication, and so on. (In the real world, of course, there may not be exactly six sides to your “hexagon”).
The goal of Hexagonal Architecture (sometimes referred to as the “Ports & Adapters” architecture) is to make it so we can easily change one aspect of the implementation – e.g., switch to a different database – without having to change any of the other aspects.
With this in mind, the different components of the system need to be loosely coupled. We don’t want, say, the UI depending on the business logic directly, nor do we want the business logic depending directly on the database. Incoming requests are handled by “ports” that forward messages or events to the right part of the core logic, and outgoing requests (e.g., database operations or web service calls) are directed through “adapters” that present our own interface – so we decide when it changes – to that external dependency.
When we consider architecture at scale, we need to remember that these separate concerns – UI, databases, web services, etc – are their own hexagons. Each has its own essential core logic (the thing that it’s about), each has its own external dependencies, and its own ports and adapters that the developers of those components concern themselves with.
It’s important not to get too hung up on the metaphor, though. Like “layered”, or “onion”, or “clean” architecture, it’s just a way of reminding us to cleanly separate the different concerns in our software, and in particular to keep implementation details like UI frameworks and databases firmly at arms length from the essential core logic.
And let’s not forget that, as software grows, there’ll likely be multiple separate concerns in the business logic itself. Managing communications with customers and fulfilling customer orders might benefit from a clean separation of concerns, even if they’re implemented in the same monolithic component. It’s not unheard of to have hexagons inside our hexagons – turtles all the way down!
If you’re serious about building your team’s capability to rapidly, reliably and sustainably evolve software to meet rapidly changing business needs, my Code Craft and Test-Driven Development live remote training workshops are HALF PRICE until March 31st 2025.