G+D Netcetera
Hexagonal Architecture
Description
Thomas Wimmer von netcetera gibt in seinem devjobs.at TechTalk eine Einführung in das Software-Architektur-Prinzip der Hexagonal Architecture.
By playing the video, you agree to data transfer to YouTube and acknowledge the privacy policy.
Video Summary
In "Hexagonal Architecture," Thomas Wimmer contrasts ports-and-adapters with layered architecture, separating an application core from the outside world and routing interactions via input/output ports and primary (driving) and secondary (driven) adapters. He shows that the key difference is the inversion of dependency direction and ties it to principles like Single Responsibility/Common Closure and Dependency Inversion, anchored by stable interfaces (ports). Viewers learn how to isolate their application core from infrastructure, make dependencies point inward, and prevent UI or persistence changes from rippling into business logic.
Hexagonal Architecture vs. Layered Architecture: Ports, Adapters, and Dependency Direction — A Recap of “Hexagonal Architecture” by Thomas Wimmer (G+D Netcetera)
Why we tuned in: The talk’s promise in one line
In “Hexagonal Architecture,” Thomas Wimmer (G+D Netcetera) delivers a concise, 10-minute introduction to Ports-and-Adapters. We listened closely to extract the practical core for engineering teams. His central point can be summarized in one line: in hexagonal architecture, all dependencies from the outside point inward to the application core. This seemingly simple inversion changes how boundaries are drawn, how changes propagate, and how stable contracts protect the core from volatile details.
What follows is our structured and technical recap: we outline the problem space in layered architectures, walk through the hexagonal model and its control flow, and highlight the design principles that make the difference tangible.
Starting point: The layered architecture refresher
Wimmer begins with a pattern every developer recognizes: layered architecture. The idea is to split a system into layers, each representing a particular technical aspect. In the typical trio—UI, business logic, data—developers working on UI shouldn’t have to care where the displayed data comes from or how it’s assembled. In practice, three layers rarely suffice for complex systems, so teams add more layers while maintaining the rule: a layer can only talk to the layer immediately below.
This creates a simple upward-to-downward delegation model. Its convenience is obvious, but so is its drawback: the dependency direction points downward. That direction silently dictates how changes propagate upward, a point Wimmer later uses to contrast with hexagonal architecture.
Hexagonal Architecture in a nutshell: Inside vs. outside; ports vs. adapters
The hexagonal (Ports-and-Adapters) architecture frames a system as an inside and an outside world. The inside is the application core; it interacts with the outside exclusively through ports. Adapters perform the actual integration with external components by implementing the contracts defined by those ports.
Wimmer walks us through a typical user interaction to make this concrete:
- A user wants the system to do something and triggers the UI adapter.
- Adapters that drive the application—commanding it to act—are called driving or primary adapters. The UI is a prime example.
- To enable the UI adapter to talk to the core, the core exposes input ports. These input ports accept commands and queries directed at the system.
- The core executes business logic: rule validation, calculations, decisions.
- If the core needs external capabilities to fulfill its job, it calls output ports toward those external systems.
- Those output ports are implemented by adapters that perform the actual integration; these are the driven or secondary adapters.
The arrangement is elegant and strict: the core defines what it needs via ports; adapters supply how it’s achieved. Technology details stay at the edges.
The crucial difference: Dependency arrows point in the opposite direction
Visual comparisons of layered and hexagonal diagrams can be misleading until you look at the arrows. Wimmer deflates the picture to make it comparable and points out the real difference: in layered architecture, business logic typically depends on the data layer; in hexagonal architecture, the outer layers (adapters) depend on the application core via ports. Put differently: in hexagonal architecture, all dependencies of the outer layers point toward the application core.
This inversion is not cosmetic. It determines how you isolate change, where contracts live, and which parts of the system you can replace without rippling side effects.
The principles behind the pattern: SRP, CCP, and DIP
Wimmer anchors the difference in well-known design principles and then lifts them to the architectural (component) level.
SRP at the architectural level
The Single Responsibility Principle says “a class should have only one reason to change.” Lift that to components: when you change one component, you don’t want unrelated components to be forced to change because of coupling. Wimmer’s layered example illustrates the problem: modify the data layer, and the business logic may have to adapt; change the business logic, and the UI might need to adjust. In layered stacks, upper layers “always have more reasons to change than the lower layers.”
Hexagonal architecture counteracts that by decoupling the core from details: changes in adapters should not affect the core, and vice versa. SRP becomes real through stable, explicit contracts.
Common Closure Principle (CCP)
Referencing Robert C. Martin’s formulation, Wimmer recalls: “you should gather together those things that change at the same times and for the same reasons, and you should separate those things that change at different times or for different reasons.” He frames CCP as SRP applied to higher-level components. The result: collect volatilities together and separate them from what should remain stable, so changes don’t leak.
Dependency Inversion Principle (DIP)
Wimmer lays out the DIP with its two rules:
1) “High-level modules should not depend on low-level modules, but both should depend on abstractions.” In layered architecture, business logic (high level) depends on the data layer (low level)—violating the rule. Also, explicit abstractions are often missing.
2) “Abstractions should not depend on details, but details should depend on abstractions.” In hexagonal architecture, ports serve as those stable abstractions. Ports must not depend on details (adapters) or even the core; instead, details (adapters) depend on ports—and the core depends on ports as well. As Wimmer puts it: introduce “stable abstractions … so that high-level concerns are not affected by changes of low-level concerns.”
Together, these rules place ports at the center as contracts, with details hanging off them instead of the other way around.
Stable vs. volatile: Contracts as the immovable center
Wimmer distinguishes between volatile and stable components. Volatile components are the ones we want to change—business logic, infrastructure code. Stable components are the contracts inside the system—interfaces (ports) and even certain system dependencies. He crystallizes this as guidance for hexagonal terms: ports should change infrequently and should not have dependencies.
This complements DIP and CCP directly: when both the core and the adapters depend on stable, dependency-free ports, modification remains local and predictable. Contracts form the keystone.
Control flow end-to-end: From user intent to external interaction
The talk’s user-driven walkthrough provides a concrete mental model:
- The UI adapter captures the intent and invokes an input port.
- The input port receives a command or query that expresses the system-directed action.
- The core processes business logic—validation, calculations—independent of storage or transport details.
- If external capabilities are required, the core calls an output port.
- A driven adapter implements that output port and handles the integration with the external system.
At every step, dependencies point inward, and details depend on abstractions.
Back-to-back comparison: How changes propagate
Wimmer’s layered vs. hexagonal contrast becomes most vivid when looking at change propagation:
- In layered stacks, a change in the data layer can bubble up because business logic depends on it. If business logic then changes, UI may need to adjust as well. In his words, “upper layers have always more reasons to change than the lower layers.”
- In hexagonal architecture, stable ports and inverted dependencies localize change. Swap a persistence technology? Replace a driven adapter. Revise UI behavior? Update a driving adapter. Evolve business rules? Keep ports steady where possible; adapters remain unaffected.
This is the practical lever: a clear dependency direction that prevents change avalanches.
Practical guidance distilled from the talk
Without code or tooling specifics, Wimmer still leaves us with implementable guidance:
- Define input ports: The core exposes explicit input ports that accept commands and queries. This is the API for driving (primary) adapters like the UI.
- Define output ports: For each external capability the core needs, define an output port. The port captures what the core requires—not how a specific system realizes it.
- Implement adapters at the edges: Driving adapters initiate calls into input ports; driven adapters implement output ports. Both depend on ports.
- Aim all dependencies inward: The core must not depend on any adapter. Adapters know ports; ports know neither core nor adapters.
- Keep ports stable and dependency free: “Ports should change infrequently and should not have dependencies.” The less they depend on, the fewer reasons they have to change.
These steps operationalize DIP and CCP in everyday architecture work.
Avoid common misunderstandings
Wimmer’s framing helps avoid several pitfalls:
- Hexagonal architecture is not layered architecture with hexagonal drawings. The crux is dependency direction, not shapes or ring counts.
- Ports are contracts, not transport protocols. Adapters perform integrations, guided by port abstractions.
- Calling adapters “primary” or “secondary” describes the flow (driving vs. driven), not importance. Both live outside the core and remain replaceable details.
Each follows from the talk’s emphasis on inverted dependencies and stable abstractions.
Quotable lines to keep top of mind
Several lines from Wimmer’s talk serve as memorable anchors:
- “Divide your software system into an inside and outside world.”
- “Adapters which drive our application are called driving or primary adapters; those that are driven are secondary adapters.”