# Hexagonal Architecture: Developing from the Domain Out

> Hexagonal Architecture, formalized by Alistair Cockburn, aims to isolate the domain. It is based on dependency inversion and dependency injection.

- Author: Mathieu Eveillard
- Published: 2025-04-23
- Updated: 2026-03-17
- Tags: Modularity
- Canonical: https://www.mathieueveillard.com/blog/architecture-hexagonale/
- Translation (fr): https://www.mathieueveillard.com/fr/blog/architecture-hexagonale.md

![Hexagonal Architecture: Developing from the Domain Out](https://www.mathieueveillard.com/blog/2025-04-23-architecture-hexagonale/architecture-hexagonale.webp)

Far be it from me to downplay Alistair Cockburn’s contributions, but it must be said: Hexagonal Architecture is as simple as it gets. And as useful as it gets. **Hexagonal Architecture is a developer’s paradise, since it allows you to very quickly deliver value to the core of the domain while abstracting away the infrastructure**.

In fact, I have a very practical use for it: as someone who frequently takes long TGV trips, I can attest that the Wi-Fi isn’t always reliable. It’s out of the question to even consider running an `npm install` when a simple web search is already a struggle, so being able to work without dependencies, using only the language itself, and creating abstractions that make sense for the business is priceless.

Let’s move on to the theory, starting with the problem statement.

## The Limitations of N-Tier Architecture

N-tier architectures have given us a perfect separation of concerns, distributed (in the example of a 3-tier architecture) among the presentation layer, the application/domain layer, and the infrastructure.

But **according to this architecture, the domain depends on the infrastructure**. This dependency leads to a twofold problem:

- Domain testing: as things stand, you have no choice but to set up a database, populate it with predefined data, and run the test. Even with an in-memory database like SQLite, it’s slow, too slow for unit tests that are supposed to run in a matter of milliseconds. This is problematic, since these are precisely the types of tests we expect to find in large numbers (by the hundreds) in the domain, where the business logic is concentrated.

- Dependency on Change: The domain describes your company’s core business. Banking, insurance, telecommunications, e-commerce, whatever it may be, that business doesn’t change every other day. It evolves slowly. On the other hand, major (breaking) version upgrades to your favorite database are frequent. It’s a shame that the domain has to be updated every time a dependency changes… You can really tell that something’s not right here.

## Dependency Inversion to the rescue

Dependency inversion solves exactly this problem. See how we go from this (in TypeScript):

```typescript
const random = (min: number, max: number): number =>
  min + (max - min) * Math.random();

const n = random(1, 10);
```

to this:

```typescript
type RandomGenerator = () => number;

type Dependencies = {
  generator: RandomGenerator;
};

const random =
  ({ generator }: Dependencies) =>
  (min: number, max: number): number =>
    min + (max - min) * generator();

const n = random({ generator: Math.random })(1, 10);
```

Here we are at the heart of the domain, and `Math.random` is the dependency causing us trouble. To remedy this, the domain defines what it needs, on its own terms, through an interface: `type RandomGenerator = () => number`. The domain thus sets the terms of the contract that the actual implementation of the dependency (`Math.random`) must adhere to when it is injected. It’s a little sleight of hand that changes everything:

- **dependency inversion**: the domain defines the interface
- **dependency injection**: the implementation is provided at call time

My example isn’t the best, because `Math.random` is a language dependency, and on top of that, the dependency implements the interface without requiring any adaptation. In practice, you’ll often need to write a few adapters, which constitute what Domain-Driven Design calls an Anti-Corruption Layer (ACL). It’s a strong word, but an apt one: you don’t want your domain to be “corrupted” by the outside world.

But wait, what exactly are these dependencies? Well, they’re the usual suspects: persistence (databases, file systems), transaction management (the Unit of Work pattern), network calls, event-driven communication, logging, as well as generating identifiers, random values, and a classic: time.

One notable consequence: **Hexagonal Architecture renders ORM (Object-Relational Mapping) libraries obsolete**, since these libraries encourage you to base your domain on the entities they expose and link to database tables. This is the opposite of Hexagonal Architecture, which allows us to build the domain without any dependencies.

## The Other Half of the Journey

This is actually only half of Hexagonal Architecture, but it’s the most distinctive part. The other half is much more familiar to us, since it involves refining the domain so that it exposes a well-structured API, which will allow it to be called in various contexts: the context of a unit test, to start with, but then, and just as easily, a REST API, a web application, a CLI, or even a batch job. **Once your domain is properly developed, you can bootstrap it in a thousand different ways**.

Writing an API follows general principles that we won’t detail here but which all point toward a highly systematic approach.

## A structure that speaks for itself

Here are a few recommendations for structuring your application in a way that makes this architecture crystal clear (_screaming architecture_):

```
├── domain/
│   ├── entities/           # including aggregates
│   ├── value-objects/
│   ├── services/
│   ├── repositories/
│   └── errors/
│
├── application/
│   └── use-cases/
│
└── adapters/
    ├── driven/
    │   ├── database/
    │   ├── filesystem/
    │   └── messaging/
    │
    └── driving/
        ├── http/           # and stub
        ├── cli/
        └── messaging/
```

The idea is to clearly distinguish between the “inside” of the hexagon (domain + application) and the “outside” (adapters):

- **The domain**: this refers to the business model and its associated behavior (ideally implemented through pure functions), as well as the contracts needed to interact with the outside world.

- **The application**: each use case reflects a user’s intent and acts as a conductor. It coordinates calls to the domain and to the outside world. But be careful: use cases don’t replace the domain; they simply call upon it (otherwise, we’re talking about an anemic domain, which is an anti-pattern).

- **Adapters**: here, it’s particularly important to distinguish between adapters that `drive` the application (an HTTP controller, a CLI, event listening) and those that are `driven` by your application (database, network calls, event emission). This rigor is especially necessary for events, which are listened to and emitted by the application.

In the end, **the domain and the application depend on nothing**. That’s what Hexagonal Architecture is, and nothing else.

You’ll find this architecture referred to as “Ports & Adapters,” notably in Alistair Cockburn’s [seminal](https://alistair.cockburn.us/hexagonal-architecture/) article. This name highlights the symmetry between dependency management on one side and the API exposed by the domain on the other. The multiplicity of the application’s ports evokes the sides of a hexagon, but at this rate, you might just as well have a dodecagon 😊

Finally, it’s worth noting that this architecture is fundamental to product development, since it allows you to focus initially on the domain alone, to quickly prove its value (e.g., by building a PoC), and to defer the choice of dependencies, particularly the database, to a later date. When the time comes, you’ll know exactly what your needs are, and your choice will be all the better for it.
