# hexagonal-architecture

> A Claude Code skill from Affaan M's everything-claude-code repo for designing Ports and Adapters systems — domain core depends on abstract ports, adapters implement them at the edges, dependency direction is always inward. Six-step workflow (use-case boundary, outbound ports first, pure orchestration, edge adapters, composition root, per-boundary tests) across TypeScript / Java / Kotlin / Go.

**Use case**: Refactor framework-heavy code into Ports-and-Adapters boundaries with use cases that don't import frameworks

**Canonical URL**: https://agentcookbooks.com/skills/hexagonal-architecture/

**Topics**: claude-code, skills, architecture

**Trigger phrases**: "refactor this Express app to hexagonal", "ports and adapters for my Java service", "decouple domain logic from the database"

**Source**: [Affaan M](https://github.com/affaan-m/everything-claude-code/tree/main/skills/hexagonal-architecture)

**License**: MIT

---

## What it does

`hexagonal-architecture` is the Ports-and-Adapters design skill in [Affaan M's everything-claude-code](https://github.com/affaan-m/everything-claude-code) — see [skills/hexagonal-architecture](https://github.com/affaan-m/everything-claude-code/tree/main/skills/hexagonal-architecture). It codifies the dependency-inversion rule that defines hexagonal: business logic is independent of frameworks, transport, and persistence; the core app depends on abstract ports; adapters implement those ports at the edges. Dependency direction is always inward — adapters → application/domain → port interfaces → domain → nothing external.

The skill names six concepts and where they live. Domain model holds business rules and entities/value objects with no framework imports. Use cases (application layer) orchestrate domain behavior and workflow steps. Inbound ports describe what the application can do (commands / queries / use-case interfaces). Outbound ports describe what the application needs from the outside (repositories, gateways, event publishers, clock, UUID). Adapters implement ports at the edges (HTTP controllers, DB repositories, queue consumers, SDK wrappers). Composition root is the single wiring location where concrete adapters get bound to use cases.

The six-step workflow: model a use case boundary with a clean input/output DTO (no transport details inside), define outbound ports *first* (every side effect is a port), implement the use case as pure orchestration receiving ports via constructor, build adapters at the edge (inbound converts protocol input, outbound maps app contracts to concrete APIs), wire in a composition root (no service-locator pattern), test per boundary (unit-test use cases with fakes, integration-test adapters with real infrastructure, E2E through inbound adapters). The suggested module layout is feature-first with explicit `domain/`, `application/ports/inbound/`, `application/ports/outbound/`, `application/use-cases/`, `adapters/inbound/`, `adapters/outbound/`, `composition/` directories.

## When to use it

- Building a new feature where long-term maintainability and testability matter
- Refactoring layered or framework-heavy code where domain logic is mixed with I/O concerns
- Supporting multiple interfaces for the same use case (HTTP, CLI, queue workers, cron jobs)
- Replacing infrastructure (database, external APIs, message bus) without rewriting business rules
- Code reviews where the question is "should this be in the use case or the adapter?"

When *not* to reach for it:

- Small scripts or one-off tooling — overhead exceeds value
- Prototypes where the boundaries don't matter yet
- Pure infrastructure code that has no domain logic (a thin proxy, a static-file server)
- Languages where the pattern's structure is unidiomatic — the skill targets TypeScript / Java / Kotlin / Go specifically

## Install

From [affaan-m/everything-claude-code](https://github.com/affaan-m/everything-claude-code) at `skills/hexagonal-architecture/`. Drop the folder into `~/.claude/skills/hexagonal-architecture/`. The skill is a design reference + code patterns; no runtime dependencies beyond whatever language the operator is targeting. TypeScript examples are the longest section in the upstream; Java / Kotlin / Go patterns follow the same boundaries.

## What a session looks like

1. **Model the use case boundary.** "Create order" with input DTO (customer ID, items, payment method) and output DTO (order ID, total, status). Transport details (`req.body`, GraphQL `context`) stay outside the boundary.
2. **Define outbound ports first.** `OrderRepositoryPort` (persistence), `PaymentGatewayPort` (Stripe wrapper), `LoggerPort`, `ClockPort`. Ports model capabilities, not technologies.
3. **Implement the use case.** `CreateOrderUseCase` receives ports via constructor. Validates application-level invariants, coordinates domain rules, returns the output DTO. No framework imports.
4. **Build adapters.** Inbound adapter: an Express route handler that converts `req.body` into the input DTO and calls the use case. Outbound adapter: `PostgresOrderRepository` implements `OrderRepositoryPort`, `StripePaymentGateway` implements `PaymentGatewayPort`. Mapping stays in adapters, not in the use case.
5. **Composition root.** Single file (`ordersContainer.ts`) instantiates adapters and injects them into the use case. No service locator. Wiring is centralized.
6. **Test per boundary.** Unit tests on the use case with fake ports. Integration tests on adapters with real Postgres / Stripe. E2E through the HTTP inbound adapter.
7. **Iterate.** Adding a CLI inbound or a queue-consumer inbound is straightforward — implement another inbound adapter that calls the same use case.

The discipline that makes it work: define outbound ports before writing the use case. The temptation is to write the use case with direct ORM calls, then "extract a repository later." The skill flips that — ports first, use case second. The use case never knows what's behind the port.

## Receipts

_TODO — to be filled in from a real session. Once a real codebase has been refactored or built using these boundaries, this section will capture: which port was missing in the first pass and caused a domain-layer leak (the most common is a Clock / time provider — domain code that calls `Date.now()` directly), how testing the use case with fake ports actually held up against real-world edge cases vs. integration tests, whether the composition root stayed clean or accumulated service-locator behavior over time, and the actual cost of adding a second inbound adapter (CLI or queue) after the HTTP one was already in place._

## Source and attribution

From [Affaan M's everything-claude-code](https://github.com/affaan-m/everything-claude-code/tree/main/skills/hexagonal-architecture) — an MIT-licensed skill collection covering harness construction, agent ops, video, payments, and platform-specific patterns.

License: MIT.

Quoting the dependency-direction rule verbatim: *"Dependency direction is always inward: Adapters -> application/domain. Application -> port interfaces. Domain -> nothing external."* The four-line invariant is the whole architecture — every other rule reduces to enforcing it.