# Ports Adapters Architecture

> Ports and Adapters (Hexagonal) architecture — isolate business logic from external concerns via explicit boundaries. TRIGGER when: user asks about ports and adapters, hexagonal architecture, hexagon architecture, Alistair Cockburn, clean architecture, onion architecture, layered architecture comparison, adapters (driving/primary adapter, driven/secondary adapter, adapter pattern boundary), ports (inbound port, outbound port, application port), layers (domain layer, application core, infrastructure layer, use case), isolation (dependency inversion in practice, testable business logic, decouple framework, boundary protection). DO NOT USE when: user asks about general OOP/SOLID principles in the abstract — use `object-oriented-programming` instead. Note: DIP from `object-oriented-programming`'s SOLID reference is the theoretical foundation of this pattern.

- Skill: `bsene/ports-adapters-architecture` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add bsene/ports-adapters-architecture`
- Raw SKILL.md: https://api.skillmd.com/api/skills/bsene/ports-adapters-architecture/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: bsene (https://skillmd.com/u/bsene)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/bsene/ports-adapters-architecture

---


# Ports and Adapters Architecture

Also called "Hexagonal Architecture" by Alistair Cockburn.

## Core Idea

Isolate your business logic in the center ("hexagon"). Everything outside—databases, web frameworks, message queues, UI—connects through explicit **ports** and **adapters**. This inversion of dependencies makes business logic reusable and testable.

---

## Structure

```
hexagon/
├── domain/              ← Business logic (center), no dependencies on outside
├── ports/
│   ├── driving/        ← Interfaces for external actors to call in
│   │   └── UserService.ts
│   └── driven/         ← Interfaces for the domain to call out
│       └── UserRepository.ts
├── use-cases/          ← Application layer, orchestrates domain
│   └── CreateUserUseCase.ts

adapters/
├── driving/            ← How external actors call the domain
│   ├── http/           ← REST controller
│   └── cli/            ← Command-line interface
└── driven/             ← How the domain calls external systems
    ├── postgres/       ← Concrete database implementation
    ├── email/          ← Email service implementation
    └── cache/          ← Cache service implementation
```

---

## Key Concepts

| Term                   | Meaning                                                                                  |
| ---------------------- | ---------------------------------------------------------------------------------------- |
| **Port**               | Interface defined by the hexagon (domain). External systems implement it.                |
| **Adapter**            | Concrete implementation of a port. Translates between domain logic and external tools.   |
| **Driving / Primary**  | Ports where external actors (HTTP client, CLI user, scheduler) call **into** the domain. |
| **Driven / Secondary** | Ports where the domain calls **out** to external systems (database, cache, email).       |

**Naming convention:** For driving ports, use `for_<action>` (e.g., `for_creating_users`).

---

## Advantages

- Business logic is framework-agnostic — swap HTTP for gRPC without touching the domain
- Testability: inject fakes for driven ports; test domain in isolation
- Clarity: dependencies flow inward only; domain never depends on adapters
- Scalability: add new adapters (new databases, new APIs) without changing the core

---

## Application Workflow

When the user wants to apply or review Ports & Adapters:

1. **Identify the boundary** — where does business logic end and infrastructure begin? The hexagon edge is the answer.
2. **Define the ports** — write interfaces (not implementations) for every outbound dependency. Name them `for_<action>`.
3. **Move logic inward** — ensure domain code imports only other domain code, never framework/DB packages.
4. **Write adapters** — one concrete class per external system, implementing the matching port interface.

## Read On Demand

| Read When                                                              | File                                                                                 |
| ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
| Before/after TypeScript example showing the refactoring                | [Before / After Example](references/before-after-example.md)                         |
| Ownership tables + tool-enforced boundaries from real NestJS hex repos | [Ownership & Boundary Enforcement](references/ownership-and-boundary-enforcement.md) |

## Read On Demand (External)

- **Alistair Cockburn** — [Hexagonal Architecture](https://alistair.cockburn.us/hexagonal-architecture/)
- **Martin Fowler** — [Ports and Adapters Pattern](https://martinfowler.com/bliki/HexagonalArchitecture.html)

### Functional Programming Variants (Haskell, OCaml, Erlang, Elixir, Clojure, ...)

Ports & Adapters maps onto FP languages as **Functional Core, Imperative Shell**: a pure,
side-effect-free core (the hexagon) surrounded by a thin shell that performs I/O and calls the
core with plain data in, plain data out. No classes/interfaces needed for ports — plain
functions (or typeclass/protocol dispatch) fill that role.

- **Janet A. Carr** — [Ports and Adapters Architecture for the Functional Programmer](https://blog.janetacarr.com/ports-and-adapters-architecture-for-the-functional-programmer/)
- **Gary Bernhardt** — [Functional Core, Imperative Shell](https://www.destroyallsoftware.com/screencasts/catalog/functional-core-imperative-shell) (screencast)
- **functional-architecture.org** — [Functional Core, Imperative Shell](https://functional-architecture.org/functional_core_imperative_shell/)
- **Community notes doc** — [Functional Core / Imperative Shell — shared notes](https://docs.google.com/document/d/1uSSL90h0vM6tLvdlnk04nZZLKfPI3By1tFdKXz_IUl8/edit?pli=1&tab=t.0)

---

## Benchmark

Scenario: `.benchmarks/scenarios/ports-adapters-001-hexagonal-refactor.md` · Run: 2026-08-31 · Log: `.benchmarks/runs/2026-08-31/ports-adapters-001-hexagonal-refactor.json`

| Model             | Without | With | Delta |
| ----------------- | ------- | ---- | ----- |
| claude-opus-4-8   | 100%    | 100% | +0%   |
| claude-sonnet-4-6 | 100%    | 100% | +0%   |
| claude-haiku-4-5  | 100%    | 100% | +0%   |

> **NEUTRAL (run 2026-08-31)**. All models 100% with and without — hexagonal refactor at ceiling. The port-naming BLOCKER did not bite here but still needs the Phase-7 scoping fix. Gate per `.agents/skills/skill-optimizer/rules/release-gates.md`.

