
Hexagonal Architecture(ヘキサゴナルアーキテクチャ)は、別名「Ports and Adapters(ポートとアダプター)アーキテクチャ」とも言われます。
アプリケーションの中核(ドメイン)を外部要素から独立させる設計思想です。
2005年にAlistair Cockburnによって提唱されました。
外部との接続を“六角形(Hexagon)”の辺=ポートとして抽象化します。
基本構造イメージ
UI / CLI / API
外部入出力(Adapter)
↓
[Inbound Port]
│
Application Core
ドメイン・ユースケース
↓
[Outbound Port]
│
DB / File / API
外部システム(Adapter)
構成要素の役割
Core(中心部:ドメイン+ユースケース)
・アプリケーションの本質的なビジネスロジック。
・外部技術(DB、Web、UIなど)に一切依存しない。
・例:TodoService, OrderProcessor, UserDomain など。
Ports(ポート)
・Coreが外部とやり取りする抽象的なインターフェース。
・「どんな操作ができるか」を定義する。
・次の2種類ある:
1. Inbound Port:外部からCoreへ(例:API → Service呼び出し)
2. Outbound Port:Coreから外部へ(例:DB保存、外部API呼び出し)
Adapters(アダプター)
・Portsの具体的な実装を担う層。
・外部の仕組み(Web・DB・外部サービスなど)を接続する役割。
・例:
1. Inbound Adapter → REST API, CLI, WebSocketなど
2. Outbound Adapter → DBアクセス, メール送信, 外部APIクライアントなど
データフロー
(例:ユーザー登録)
[REST API]
↓
[Inbound Adapter]
↓
[Inbound Port]
↓
[Application Core]
↓
[Outbound Port]
↓
[DB Adapter]
↓
[Database]
構造イメージ
📦 hex-todo/
┣ 📂 core/
┃ * ドメイン+ユースケース
┃ ┣ todoService.js
┃ ┗ ports/
┃ ┣ todoRepositoryPort.js
┃ ┃ * Outbound Port
┃ ┗ todoInputPort.js
┃ * Inbound Port
┣ 📂 adapters/
┃ ┣ inbound/
┃ ┃ ┗ restTodoController.js
┃ ┗ outbound/
┃ ┗ memoryTodoRepository.js
┗ index.js
core(ビジネスロジック)は「Express」や「MongoDB」などに依存せずに動作するのがポイント。
メリット
・ビジネスロジックの独立性が高い(DBやUI変更の影響を受けない)。
・テスト容易性が高い(モックPortでユニットテスト可能)。
・拡張性が高い(異なるUIやDBを簡単に差し替えできる)。
・Clean Architectureの基盤思想になっている。
デメリット
・小規模アプリでは構成がやや複雑(設計コスト増)。
・「Port」と「Adapter」の関係を明確に分離する設計力が必要。
・クラスやファイルが増えやすい。
サンプルコード
ファイル階層
src/
├ domain/
│ ├ model/
│ │ └ User.js
│ └ service/
│ └ UserService.js
├ application/
│ └ UserUseCase.js
├ infrastructure/
│ ├ persistence/
│ │ └ UserRepositoryMemory.js
│ └ controller/
│ └ UserController.js
└ index.js
domain/model/User.js
export class User {
constructor(id, name, email) {
this.id = id
this.name = name
this.email = email
}
}
domain/service/UserService.js
export class UserService {
constructor(userRepository) {
this.userRepository = userRepository
}
createUser(name, email) {
const id = Date.now()
const user = { id, name, email }
this.userRepository.save(user)
return user
}
getAllUsers() {
return this.userRepository.findAll()
}
}
application/UserUseCase.js
export class UserUseCase {
constructor(userService) {
this.userService = userService
}
registerUser(name, email) {
return this.userService.createUser(name, email)
}
listUsers() {
return this.userService.getAllUsers()
}
}
infrastructure/persistence/UserRepositoryMemory.js
export class UserRepositoryMemory {
constructor() {
this.users = []
}
save(user) {
this.users.push(user)
}
findAll() {
return this.users
}
}
infrastructure/controller/UserController.js
export class UserController {
constructor(userUseCase) {
this.userUseCase = userUseCase
}
create(req, res) {
const { name, email } = req
const user = this.userUseCase.registerUser(name, email)
res(user)
}
list(req, res) {
const users = this.userUseCase.listUsers()
res(users)
}
}
index.js(エントリーポイント)
import { UserRepositoryMemory } from './infrastructure/persistence/UserRepositoryMemory.js'
import { UserService } from './domain/service/UserService.js'
import { UserUseCase } from './application/UserUseCase.js'
import { UserController } from './infrastructure/controller/UserController.js'
// 依存注入
const repo = new UserRepositoryMemory()
const service = new UserService(repo)
const useCase = new UserUseCase(service)
const controller = new UserController(useCase)
// 疑似HTTPリクエスト
controller.create({ name: 'Taro', email: 'taro@example.com' }, console.log)
controller.create({ name: 'Hanako', email: 'hanako@example.com' }, console.log)
controller.list({}, console.log)
ポイント解説
ドメイン層(domain/)
→ ビジネスロジックを保持。外部技術とは無関係。
アプリケーション層(application/)
→ ユースケース単位の操作を提供。
インフラ層(infrastructure/)
→ データ永続化やI/O操作を担当。
ポートとアダプタ
→ UserServiceがポート、UserRepositoryMemoryがアダプタ。
Layered Architectureとの違い
| 比較項目 |
Layered Architecture |
Hexagonal Architecture |
| 構造 | 上下の層構造 |
中心から外へ広がる六角構造 |
| 依存方向 |
一方向(上→下) |
ドメインを中心に内向き依存 |
| 柔軟性 |
中程度 |
高い(外部切替が容易) |
| 主眼 |
責務の階層分離 |
技術依存の分離と独立性 |
| 代表例 |
MVC, 3層構造 |
Clean Architecture, Onion Architecture |
あとがき
Layered Architectureをより発展させたデザインパターンです。
テスト効率が良いですが、ファイルが増えがちになるのは、なんとも悩ましい感じですね。
0 件のコメント:
コメントを投稿