Software Architecture

What Is Hexagonal Architecture? Ports, Adapters & Clarity

What Is Hexagonal Architecture? Ports, Adapters & Clarity

Business logic has a habit of ending up inside user interface code, where automated tests can’t reach it. Alistair Cockburn described a fix in 2005 under the name ports and adapters, and most people now call it hexagonal architecture. The pattern isolates the application core from databases and frameworks, along with the user interface, and all input and output passes through ports that adapters implement.

Cockburn’s goal was to let a user and a test script drive the same business logic, and a batch job too.

One detail gets misunderstood a lot. Ports belong to the core, while adapters sit outside its boundary, a point Cockburn stresses in his 2025 iSAQB keynote.

Compared with layered architecture, the pattern drops the top-to-bottom stack and works with an inside and an outside instead.

What Is Hexagonal Architecture?

YouTube player

The core of the application never deals with databases or frameworks directly, and it doesn’t care about the user interface either. Input and output both go through ports, and adapters implement those ports.

The core never learns what sits on the other side of a port. A test script looks the same to it as a browser does, or a message queue.

Cockburn’s stated aim was an application that people, programs, test scripts and batch jobs can drive equally well, and that can be built and tested without its real devices and databases (Alistair Cockburn, 2005).

Cockburn’s paper lists ports and adapters as the pattern name, with hexagonal architecture as the alternative. Juan Manuel Garrido de Paz (2018) prefers the first, since it names the actual building blocks.

Who Created Hexagonal Architecture, and When

The write-up is HaT Technical Report 2005.02 by Alistair Cockburn. It is dated 2005-09-04 and marked version 0.9, which means it was open to reader comments.

Secondary sources don’t agree on the day. Wikipedia’s reference lists 2005-04-01, and one explainer gives September 5, 2005. Only the year matches across all three.

What drives the global software industry?

Uncover software development statistics: industry growth, methodology trends, developer demographics, and the data behind modern software creation.

Discover Software Insights →

Wikipedia adds that the idea first circulated on the Portland Pattern Repository wiki before the 2005 rename to ports and adapters.

Cockburn documented it as a formal design pattern, classified as object structural, with intent, motivation, and structure sections.

The problem behind it was business logic leaking into user interface code. Logic that depends on field sizes and button placement cannot be reached by automated tests.

The same entanglement shows up on the data side. When the database server went down or was replaced, programmers could not work.

Cockburn and Garrido de Paz later published a full-length treatment, Hexagonal Architecture Explained, in 2024.

Why It Is Drawn as a Hexagon

The number six carries no meaning.

Cockburn chose the shape to leave room for ports and adapters on every side and to break away from the one-dimensional layered picture (Alistair Cockburn, 2005). Garrido de Paz (2018) relays his reasoning. A square suggests top-down or left-right, and shapes with more sides are harder to draw.

In the 2005 paper, Cockburn reports between two and four ports in real applications, with four the most he had met so far. Six is not a rule. The ports follow whatever conversations the application really has.

Many explainers describe domain, ports, and adapters as layers, yet Garrido de Paz (2018) points out that the pattern says nothing about layers.

The asymmetry Cockburn cares about is inside versus outside, not left versus right.

What Are the Parts of Hexagonal Architecture?

YouTube player

The application core sits in the middle with ports along its edge, and adapters translate for whatever is outside.

Actors such as users, test scripts, databases, and other services sit outside. They reach the core only through a port, by way of an adapter.

The Application Core

YouTube player

The core holds everything the business problem needs and nothing else.

That means the domain model and the use cases, plus the business rules that go with them. No framework imports and no SQL. HTTP types stay out too.

The pattern does not dictate how the inside is organized. Layers and feature modules both fit, and so do domain-driven design tactical patterns (Garrido de Paz, 2018).

Cockburn adds a practical rule. Write use cases at the boundary of the core and keep any outside technology out of them. Written that way, they come out shorter and cheaper to maintain, and they stay stable longer.

Ports

Each port is a purposeful conversation between the core and the outside, written as an interface that belongs to the core.

Port names describe purpose, never technology. Garrido de Paz (2018) gives “adding products to the shopping cart” and “obtaining information about orders” as examples, along with “sending notifications.”

How many ports an application has is largely a matter of taste. Cockburn’s examples show how few a real application needs. A weather alert system gets by with four, covering the weather feed, the administrator, the notified subscribers, and the subscriber database. A coffee machine controller also has four (user, recipe and price database, dispensers, coin box). A hospital medication system needs only three, for the nurse, the prescription database, and the medication dispensers.

He sees no particular damage in picking the “wrong” number, and he favors a small one.

One drawing error is common. The correct picture is actor, then adapter, then the port on the edge of the core, so drawings that place the port outside the adapter get it backwards.

Adapters

Each adapter translates between one specific technology and a port. A GUI turns clicks into port calls, and an SQL adapter turns port calls into queries.

One port usually has several adapters, and Cockburn lists these for the same kinds of ports.

  • GUI
  • Test harness
  • Batch driver
  • HTTP interface
  • In-memory database
  • Real database

Adapters live outside the application boundary. Cockburn’s 2025 iSAQB keynote covers why they always do (iSAQB, January 2026).

Garrido de Paz (2018) recommends at least two adapters per port, one real and one test double.

Driving and Driven Sides

Who starts the conversation decides which side an actor sits on.

Cockburn borrowed the terms from use case vocabulary. A primary actor takes the application out of idle to perform a function, while a secondary actor is one the application drives to fetch answers or send notifications.

AspectDriving sideDriven side
Other namesPrimary, inbound, driverSecondary, outbound
Who starts the conversationThe actorThe application
Typical actorsUsers, test scripts, batch jobs, other applicationsDatabases, mail servers, message brokers
Typical adaptersGUI, REST controller, CLI, test runnerSQL repository, email sender, event publisher
Role of the portAPI the core offers (use case boundary)Interface the core requires (SPI)

Garrido de Paz (2018) splits driven actors by whether the core reads anything back. A repository is one the core also reads from, as with a database or any storage device. A recipient only receives, so the core sends and moves on, as with an SMTP server.

How Does a Request Travel Through a Hexagonal Application?

A request enters through a driving adapter, which converts it into a technology-neutral call on a driving port. The core runs the use case and calls a driven port when it needs storage or an outside service. A driven adapter then does the technology-specific work.

Garrido de Paz (2018) uses a small web application where employees assign tasks to each other and the assignee gets an email. Here is how a request moves through it.

  1. The web controller (a driving adapter) receives the HTTP request to assign a task.
  2. The controller converts it into a plain call on the driving port for assigning tasks.
  3. The core applies the business rules inside the use case.
  4. The core calls the driven port for sending notifications, without knowing what implements it.
  5. The email adapter, which implements that port, builds the message and hands it to the SMTP server.
  6. The result returns through the driving port, and the controller turns it into an HTTP response.

Every source-code dependency points inward. The driving adapter uses the core’s port, the driven adapter implements the core’s port, and the core depends on nothing outside itself.

That is the dependency inversion principle at work on both sides of the core. Cockburn’s 2005 paper cites Robert C. Martin’s formulation and Martin Fowler’s dependency injection pattern as the ways to build swappable driven adapters.

The wiring happens once, at startup. A composition root picks each driven adapter and has it injected into the core through its constructor, then hands the finished core to each driving adapter.

Cockburn’s 2005 sample passes the repository into the application’s constructor by hand. He mentions the Spring framework as a configuration-driven alternative.

The two sides are not wired alike. The driving adapter knows the core and calls it, while the core knows only the interface of a driven port, never the adapter behind it (Garrido de Paz, 2018).

A port shaped around one technology drags that technology into the core. Cockburn’s weather-alert team fixed this by defining interfaces by purpose instead of by technology, which let them add an HTTP feed and an email notifier as plain new adapters (Alistair Cockburn, 2005).

How Does Hexagonal Architecture Make the Core Testable?

YouTube player

Tests call the driving ports directly and swap every driven port for an in-memory stand-in, so no database or web server has to run, and neither does the user interface.

A test harness is just another driving adapter. Cockburn’s 2005 paper names automated harnesses such as FIT and Fitnesse as adapters that fit the same port as a GUI.

On the driven side, Cockburn’s own Java sample uses a hand-written in-memory repository instead of a mocking library. Unit tests built on mocking the driven adapters follow the same idea with generated doubles, for example through Mockito in Java.

Garrido de Paz (2018), drawing on Nat Pryce’s test terminology, ties each kind of test to a different part of the pattern.

  • Unit tests cover single objects inside the core.
  • Acceptance tests go through the driving ports, which means the core in isolation.
  • Integration tests exercise the adapters.
  • System tests run core and adapters together, plus deployment and startup.

Adapters get integration tests against the real technology, since a mock cannot show that an SQL adapter writes the right columns. The Testcontainers library covers this by starting real databases and message brokers in Docker containers, with libraries for Java, Go, .NET, Node.js, Python, Rust, Ruby, Haskell, Clojure, Elixir, PHP, and native languages (Testcontainers documentation, as of October 2026).

The catch is that Testcontainers needs a supported Docker environment on the developer machine or the CI runner.

Tests also guard the boundary itself. Cockburn’s 2025 iSAQB keynote treats tests as the first and most important user of the application (iSAQB, 2026), and his 2005 paper says automated regression tests are what detect business logic creeping back into the presentation layer.

ArchUnit turns the dependency rule into a failing build in Java. The library analyzes bytecode and checks dependencies between packages, classes, layers, and slices.

Its Library API includes onion architecture rules, which Baeldung’s ArchUnit introduction notes are also known as hexagonal or ports and adapters. ArchUnitNET covers the same job on .NET (both as of October 2026).

Mocked ports prove the core’s logic. They say nothing about whether an adapter works, so the adapter tests above are not optional.

Hexagonal Architecture vs Layered, Onion, and Clean Architecture

YouTube player

Onion and clean architecture follow the same rule as hexagonal, which is that dependencies point toward the business logic. They differ in age, in how much structure they prescribe, and in whether they say anything about the inside of the application at all.

PatternSourceCore ideaStructure prescribed
LayeredTraditional conventionEach layer depends on the one belowStacked tiers, top to bottom
HexagonalCockburn, 2005Ports and adapters around the coreNone inside the core
OnionPalermo, 2008All coupling points to the centerDomain model plus rings around it
CleanMartin, 2012The Dependency RuleFour schematic circles, use cases first-class

In the traditional layered stack, each layer depends on the one beneath it, so the UI ends up coupled to data access through the business logic (Palermo, 2008).

Hexagonal architecture flips that direction. Infrastructure depends on the core, never the reverse.

Palermo’s 2008 post names the shared premise with hexagonal himself, which is externalizing infrastructure behind adapter code. Several explainers call the two patterns the same thing, but they are separate.

Where hexagonal architecture stops at the boundary, Palermo’s onion model names rings inside the core. The domain model sits at the center, repository interfaces form the first ring around it, and UI, infrastructure, and tests live at the edge.

Thoughtworks’ Rahul Garg (2022) reads that as a step beyond hexagonal architecture.

Martin’s 2012 post lists hexagonal architecture among its inputs and merges them into one concentric diagram.

The result, Martin’s clean architecture, states the Dependency Rule outright. Source code dependencies point only inward, and no inner circle may name anything from an outer one. It also makes use cases a circle of their own, a choice the original ports-and-adapters text leaves open.

MVC and its variants apply the same idea only on the primary side, according to Cockburn’s related-pattern notes (2005).

The sources don’t fully agree on layers. Martin (2012) says all of these architectures divide software into layers, with at least one for business rules and one for interfaces.

Cockburn’s paper frames hexagonal architecture as inside versus outside and stops at the boundary. The split comes from scope. Martin’s diagram merges several styles into one picture, while the ports-and-adapters text never describes the core’s interior.

The practical verdict is plain. Milan Jovanovic’s August 2026 comparison finds that .NET projects built on any of the three end up with nearly identical project structures, and advises picking the one the team understands best.

How to Structure a Hexagonal Architecture Project

YouTube player

Garrido de Paz (2018) splits the project into a core that holds the domain, the use cases and every port interface, one module per adapter (a REST controller, an SQL repository, an email sender), and a startup module that builds the whole system as the composition root. The core depends on nothing, adapters depend on the core, and the startup module depends on all of them.

If the language cannot expose only the ports from the core, Garrido de Paz splits it again into a ports module and an implementation module. That adds one more module to maintain.

Separate modules are optional. Jovanovic (2026) notes the layering is logical, so the dependency rule can hold inside a single project through folders and architecture tests, though separate projects make a violation harder to introduce.

Build the pieces in this order.

  1. Write the use cases against the driving ports, using a test driver and in-memory stand-ins for every driven port. The core is complete at this point.
  2. Add the real driving adapters, such as a web UI or REST API, while the in-memory driven adapters stay in place.
  3. Add the real driven adapters, such as the database and email sender, and check them with the test driver from step 1.
  4. Connect real adapters on both sides and run the application end to end.

Cockburn’s 2005 sequence (test harness with an in-memory database, then a GUI, then integration tests against a real database, then live use) and Garrido de Paz’s 2018 sequence agree on this order. Two sources written 13 years apart, one answer.

Do not design every driven port up front. Their exact needs surface while the driving ports are being implemented (Garrido de Paz, 2018).

A few mistakes keep breaking the structure.

  • Over-porting is the most common, meaning a port for every dependency, even a logger with one implementation. A DEV Community practitioner guide (April 2026) offers a test. Will a second adapter ever exist?
  • Ports sometimes carry query mechanics, such as methods that take filter dictionaries and offsets, or even raw query strings. Name them for business intent instead, such as finding pending orders.
  • Framework data can cross the boundary. Martin’s post sets the rule for clean architecture, and it carries over. Pass plain data structures, never entities or database rows.
  • An anemic core shows up when arithmetic and status changes live in a service. A business rule has leaked out of the domain object.

When Does Hexagonal Architecture Pay Off?

YouTube player

It pays off in long-lived systems with real business rules, where the same logic has to outlast several infrastructure changes.

Project lifespan, not language or framework, is what the sources keep pointing to. Palermo’s onion variant fits long-lived business applications with complex behavior, not small websites (Palermo, 2008). Medium and large projects with long life cycles repay the extra structure, according to Garrido de Paz (2018). The DEV Community guide (April 2026) goes furthest and says the upfront cost is recovered over a codebase horizon of 2 or more years.

Only that guide gives a number, and it reads as a rule of thumb, not a measurement.

Palermo’s post supplies the reasoning behind the clock. The industry had historically changed data access techniques at least every three years, so a healthy long-lived system should expect to modify data access within that window (a 2008 observation, not a current measurement).

Garrido de Paz (2018) ties the benefit to maintainability. Separation of concerns makes the code to change easier to find, and he argues it lowers technical debt over time.

A port backed by several real implementations earns its interface quickly, a payment gateway with multiple processors and an in-memory adapter being the example.

Teams doing domain-driven design seriously get the most out of it, according to the same practitioner guide. Without that discipline, the guide warns, the pattern tends to produce arbitrary layering.

Garrido de Paz (2018) lays out the trade-off this way.

On the plus side, the core runs without external devices, which makes testing easy. A technology swap is a configuration change or one new adapter. Framework and database choices can wait while the core is built, and an upgrade touches the adapter, not the application.

The costs are real too. There are more modules, with explicit dependencies between them. Compiling, testing, and assembling that many modules slows large projects down, and the extra calls and object mapping between port and technology add indirection.

When Is Hexagonal Architecture the Wrong Choice?

It is a poor fit when there are no real business rules to protect, and it struggles while the domain is still being discovered. Small or short-lived systems rarely repay it either, and neither do teams without the documentation habits the pattern demands.

A domain of plain data containers gives the core nothing to protect, while the structure’s cost stays. In one Python example, adding a single field touched 5 files, against 2 in a flat FastAPI app (DEV Community, April 2026).

When input and output adapters cannot be specified yet, ports get redefined again and again (JDriven, 2022).

For small or short-lived systems, the setup cost can fail to pay off at all (DEV Community, 2023). Jovanovic (2026) puts it bluntly. A CRUD API with 5 endpoints does not need 4 projects.

The pattern assumes each business feature is defined and recorded before it is built. A resource-starved team that skips that step ends up skipping the isolation too, and JDriven argues the result is worse than a traditional layered codebase.

Rotating or outsourced teams have a different problem. Newcomers must read up on design decisions before they can contribute (JDriven, 2022).

Debugging gets harder, since tracing a bug means following the call across ports and adapters instead of reading one call chain. JDriven flags this as a recurring cost, mostly for teams not fully experienced with the pattern.

Speed matters less than people fear. The extra indirection adds calls, but a February 2023 DEV Community post by Alex Pushkarev calls the effect usually negligible and reserves the concern for real-time and trading systems.

Sources split on a port with a single adapter. Garrido de Paz (2018) argues the pattern still pays when technology never changes, because mock adapters cover unavailable services and adapters can differ per runtime environment.

The DEV Community guide (April 2026) treats a port with one adapter and no second planned as interface cost without benefit, and suggests testing against a test database instead. The positions split on one point, which is whether an in-memory test double counts as the second adapter.

JDriven’s closing advice runs against the obvious choice. A simpler, well-kept layered architecture can give a better codebase than forcing hexagonal architecture where it does not fit.

The same February 2023 post points that way for legacy N-tier systems, where the terminology looks alien and integration is hard.

Build flat first, then refactor toward ports once the boundaries are clear. Extract a port when a second adapter appears or when tests become painful without one (DEV Community, April 2026).

Hexagonal Architecture FAQ

Is hexagonal architecture the same as domain-driven design?

YouTube player

No, though the two get mentioned together a lot. Hexagonal architecture decides where the application boundary sits, and domain-driven design has nothing to say about that, since it is an approach to modeling the business domain.

DDD is not required. The pattern prescribes nothing about the inside of the core, so a domain-driven model fits as easily as a plain CRUD design (Garrido de Paz, 2018).

Does hexagonal architecture work with microservices?

Yes, as the internal structure of each service. A call to another service is simply a driven adapter, which Garrido de Paz (2018) calls an app-to-app adapter.

Where the services split is a separate decision, covered by microservices architecture. Wikipedia notes that some authors trace microservices back to the pattern.

Which programming languages support hexagonal architecture?

Any language with interfaces and a way to pass implementations in will work. Cockburn’s 2025 iSAQB keynote uses Java and Ruby examples.

A 2026 DEV Community series applies the pattern in Python, and Jovanovic’s 2026 comparison covers .NET.

How is a port different from an ordinary interface?

A port sits at the application boundary. An interface between two classes inside the core is ordinary design, not a port.

Granularity is open. You can have one port per use case, or one port that groups several (Garrido de Paz, 2018). What defines it is the purposeful conversation with an outside actor.

Which book covers hexagonal architecture in depth?

Hexagonal Architecture Explained by Alistair Cockburn and Juan Manuel Garrido de Paz (2024) is the full treatment from the pattern’s author. It includes code samples and FAQ chapters on related concepts.

Cockburn’s 2005 paper remains the short primary source.

Starting Hexagonal Architecture in an Existing Codebase

Adopting it in an existing codebase starts with one service, the one with the most business logic and the most infrastructure coupling, and whose tests are the hardest to write.

A DEV Community practitioner guide (April 2026) recommends that single starting point over a rewrite. Extract that service first and leave the rest alone, then compare the result with what you had. Is it cleaner, and faster to test? Continue only if it is.

That choice targets the code that hurts most, so the gain is measurable before any other service changes.

The guide also sets a stopping rule. When the extracted service shows no gain, stop instead of pushing harder.

The price is a codebase running two structures side by side until the migration ends.

Bogdan Sandu

Stay sharp. Ship better code.

Every week: one curated article, one tool worth knowing, one tip you can use tomorrow. No noise, no padding.