ddd-architecture-hexagonal について
バックエンドアーキテクト、テックリード、DDD 実践者向けのヘキサゴナルアーキテクチャガイドです。ポートとアダプターの境界、実装手順、テスト方法に加え、複数の入口や交換可能なアダプターの例を扱います。
主な機能
- 01ポートとアダプターの層構成ルール
- 02ドメイン層の依存分離
- 03複数入口の実装手順
- 04ポート粒度と例外変換の規則
- 05ポートのモックと統合テスト
- 06アーキテクチャ例と移行ガイド
活用シーン
- 01REST、CLI、MQ の入口で業務ロジックを共有
- 02DB や外部サービスの交換時にドメインを分離
- 03DB や HTTP を使わずドメインロジックをテスト
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ full-stack-skills/ ddd-skills --skill ddd-architecture-hexagonalインストール後、Claude Code で/ddd-architecture-hexagonalを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
agents/openai.yamlexamples/01-order-hexagonal-complete.md02-user-registration-example.md03-multi-entry-points-example.md04-adapter-swapping-example.md05-port-swapping-test.md06-monolith-simple.md07-monolith-complex.md08-monolith-multi-module.md09-microservice-simple.md10-microservice-complex.md11-microservice-multi-module.md12-microservice-complex-multi.mdreferences/01-port-definitions.md02-primary-adapters.md03-secondary-adapters.md04-domain-model.md05-application-services.md06-di-configuration.md07-testing.md08-migration.md09-clean-ddd-hexagonal-hexagonal.md10-ddd4j-hexagonal-structure.md11-directory-structure.md12-hexagonal.md13-ports-adapters-code.md詳細
DDD アーキテクチャ — ヘキサゴナル(ポート&アダプター)
Alistair Cockburn(2005) — ビジネスロジックをポート(Ports)で隔離し、外部システムをアダプター(Adapters)経由で接続します。対象ユーザー:バックエンドアーキテクト/テクニカルリード(チーム規模 > 5 人)、DDD 実践者。カスタマイズ方法:技術スタック/エントリーポイントの種類/チーム規模という 3 つの設定軸を提供します。このスキルを読み込む際は、適合性を確認するため、まず Workflow と When to Use を読むことをおすすめします。
「ユーザー、プログラム、自動テスト、バッチスクリプトのいずれからも、アプリケーションを同等に駆動でき、最終的な実行環境やデータベースから切り離した状態で開発・テストできるようにします。」 — Alistair Cockburn
Workflow
Step 1: プライマリーアダプターによるプロトコル変換 — REST Controller/CLI がリクエストを受け取り、パラメーター検証とプロトコル変換を行います
Step 2: インバウンドポートの呼び出し — アダプターが UseCase インターフェースを呼び出し、アプリケーション層のユースケース編成を開始します
Step 3: アプリケーションサービスによる編成 — Application Service がユースケースを編成し、ドメインモデルを呼び出してビジネスロジックを実行します
Step 4: ドメインモデルの実行 — 集約ルート/エンティティがビジネスロジックを実行し、結果またはドメインイベントを出力します
Step 5: アウトバウンドポートによる永続化 — アプリケーションサービスが Repository インターフェースを通じてドメインの状態を保存します
Step 6: セカンダリーアダプターによる技術的実装 — Adapter が DB/MQ/外部 API の技術的な実装を行います
定義の検証方法:データベースと HTTP から切り離した状態で、ドメイン層の単体テストをすべて実行できれば、境界は正しく設定されています。
Rules
- 依存関係のルール:依存の方向は必ず外側から内側に向かいます——Adapter → Application → Domain。Domain 層には外部依存を持たせません。
- ポートの粒度に関するルール:各ポートの責務は単一にします。メソッドが 10 個以上あるポートは、すぐに分割する必要があります。
- 例外変換のルール:技術的な例外(SQLException、TimeoutException)は必ず Adapter 内部で捕捉してドメイン例外に変換し、上位層に漏らしてはいけません。
When to Use
✅ 適したシナリオ
- 複数のエントリーポイントを持つシステム(REST + CLI + MQ + gRPC):複数の外部システムが同じビジネスロジックを同時に駆動します
- インフラストラクチャが頻繁に変更される場合(DB/MQ/キャッシュの変更):Adapter の実装を差し替えるだけで済み、Domain の変更は不要です
- 最高レベルのテスト容易性が求められる場合:ポートをモックすれば、データベースに依存せず、すべてのドメインロジックをテストできます
- マイクロサービスアーキテクチャの標準化:チーム規模が 5 人を超え、各サービスで一貫したアーキテクチャ規約が必要です
- チームが抽象設計に慣れている場合:過度な抽象化を避けながら、適切なポートの粒度を設計できます
Boundary
⚠️ 条件が必要
- チームが抽象設計を理解していること:適切なポートの粒度には、ドメイン知識と抽象化能力が必要です。そうでない場合、定義が不適切になる可能性があります
- プロジェクトの規模が適度であること:小規模なプロジェクトでは Layered のほうがシンプルです。ヘキサゴナルアーキテクチャでは間接層のコストが増加します
- DDD のドメインモデルとの連携が必要であること:Port インターフェースの定義には、事前にドメインモデリングの知識が必要です
- DI コンテナーが適切にサポートされていること:Spring/Guice などのフレームワークを使うと、アダプターの組み立てを簡素化できます
❌ 適さないケース(代替案を併記)
- 単一の REST API + シンプルな CRUD →
ddd-architecture-layeredを使用します - 中国語圏の企業で MyBatis エコシステムを使用する場合 →
ddd-architecture-colaを使用します - UseCase と Entity を厳密に分離する必要がある場合 →
ddd-architecture-cleanを使用します - 迅速なプロトタイプ作成/MVP 段階 → 従来の 3 層アーキテクチャをそのまま使用します(ヘキサゴナルアーキテクチャのオーバーヘッドを導入しません)
コア原則
3 つの主要な抽象概念
Driving Side (REST/CLI/gRPC/Test) → Domain Core ← Driven Side (PostgreSQL/RabbitMQ/Redis/Stripe)
| 概念 | 説明 | コードでの表現 |
|---|---|---|
| Port(ポート) | ドメイン層で定義するインターフェース | interface OrderRepository |
| Primary Adapter(プライマリーアダプター) | 外部からシステムを駆動する方法 | REST Controller、CLI Command、gRPC Service |
| Secondary Adapter(セカンダリーアダプター) | システムから外部を駆動する方法 | JPA RepositoryImpl、Kafka Producer、StripeClient |
Strong Port と Weak Port
// ❌ Weak — 泄漏 SQL 概念
interface OrderRepository { List<Order> findByQuery(String sql, Map<String, Object> params); }
// ✅ Strong — 纯领域概念
interface OrderRepository {
Optional<Order> findById(OrderId id);
List<Order> findByCustomerId(CustomerId customerId);
void save(Order order);
}
ディレクトリ構成
{project}/
├── {project}-domain/ # 领域核心 + 端口定义(零框架依赖)
│ └── port/{inbound, outbound}/ # ★ 端口接口
├── {project}-application/ # 应用层(UseCase 实现)
├── {project}-adapter/ # 适配器层
│ ├── inbound/ # ★ 主适配器(REST/CLI/gRPC/MQ)
│ └── outbound/ # ★ 次适配器(Persistence/Messaging/External)
└── {project}-configuration/ # 配置层(DI 装配)
開発規約
レイヤーの責務
| レイヤー | 依存先 | 許可される処理 | 禁止される処理 |
|---|---|---|---|
| Domain | なし | エンティティの振る舞い、値オブジェクト、ドメインイベント、ポートの定義 | フレームワークのアノテーション、SQL、HTTP の import |
| Application | Domain | ユースケースの編成、トランザクション管理、ポート呼び出し | if/else によるビジネス判断、DB の直接操作 |
| Adapter | Application | プロトコル変換、パラメーター検証、例外マッピング | ビジネスロジックを含めること、Domain 内部の状態を直接操作すること |
| Configuration | すべて | DI の組み立て、Profile の設定 | ビジネスコードを含めること |
コーディング規約
// 入站端口(Domain)
public interface CreateOrderUseCase { OrderCreatedResult execute(CreateOrderCommand command); }
// 出站端口(Domain)
public interface OrderRepository { void save(Order order); Optional<Order> findById(OrderId id); }
// 应用服务(实现入站端口,注入出站端口)
@ApplicationService
public class CreateOrderService implements CreateOrderUseCase {
private final OrderRepository orderRepository;
@Override @Transactional
public OrderCreatedResult execute(CreateOrderCommand command) {
Order order = Order.create(command.getCustomerId());
orderRepository.save(order);
return OrderCreatedResult.from(order);
}
}
// 主适配器(仅协议转换)
@RestController
public class OrderController {
@PostMapping("/orders")
public ResponseEntity<CreateOrderResponse> create(@RequestBody @Valid CreateOrderRequest req) {
var result = createOrderUseCase.execute(req.toCommand());
return ResponseEntity.status(201).body(CreateOrderResponse.from(result));
}
}
// 次适配器(技术实现)
@Repository
public class PostgresOrderRepository implements OrderRepository {
@Override public void save(Order order) { jpaRepo.save(mapper.toPO(order)); }
}
導入手順
Phase 1: 定义端口(1-2 天)→ 入站端口(UseCase) → 出站端口(Repository/Gateway)
Phase 2: 领域模型(2-3 天)→ 聚合根/实体/值对象 → 领域服务 → 领域事件
Phase 3: 应用服务(1-2 天)→ UseCase 实现(注入端口,编排调用)
Phase 4: 适配器(2-4 天) → 主适配器(REST/gRPC/CLI) → 次适配器(DB/MQ/External)
Phase 5: DI 装配 + 测试(1-2 天)→ DI 配置 → 端口 Mock 测试 → 适配器集成测试
Security & Stability
- このスキルはドキュメントのみで構成されるアーキテクチャガイドであり、ユーザーデータの収集、処理、アップロードは行いません。
- すべてのコードテンプレートは学習用の参考資料です。外部サービスの URL、キー、認証情報は環境変数の設定に置き換えてください。
- Port/Adapter による隔離により、ドメインロジックが HTTP/DB/MQ ライブラリに直接依存することを防ぎます。すべての I/O は Adapter 層で処理します。
- Secondary Adapter を実装する際は、必ず接続タイムアウトと読み取りタイムアウトを設定し、重要な経路にはサーキットブレーカーパターンを実装してください。
Gotchas(よくある落とし穴 8 件)
- ポートの定義が広すぎる:各ポートの責務は 1 つだけにします。メソッドが 10 個以上あるポートは「神ポート」とみなし、すぐに分割してください。
- プライマリーアダプターにビジネスロジックを含める:Controller が行ってよいのはパラメーター変換と UseCase の呼び出しだけです。if/else によるビジネス判断を含めてはいけません。
- セカンダリーアダプターでの例外変換を忘れる:技術的な例外(SQLException、TimeoutException)は必ずアダプター内部でドメイン例外に変換してください。
- ドメイン層がフレームワークに依存する:Domain モジュールでは Spring/JPA/MyBatis のアノテーションを import できません。
- ポートの命名に一貫性がない:インバウンドポートは動作を表す名前(
CreateOrderUseCase)、アウトバウンドポートはリソースを表す名前(OrderRepository)にします。 - トランザクションをアダプター層に置く:トランザクションはアプリケーション層が制御し、アダプターはトランザクション管理を担当しません。
- 過度な抽象化:「将来変更するかもしれない」という理由だけで、先回りしてポートを作成してはいけません。実際に置き換えが必要になったときにポートを抽出してください。
- ユーザーの要件が不明確なときは、まず仮定を示す:ユーザーが技術スタック/エントリーポイントの種類/チーム規模を明示していない場合は、まず仮定に基づいた案を提示してから、追加で質問してください。
FAQ(よくある質問 8 件)
Q1: ヘキサゴナルアーキテクチャとクリーンアーキテクチャの違いは何ですか? A: ヘキサゴナルアーキテクチャは Cockburn が提唱したもので(2005)、ポートとアダプターが中核です。クリーンアーキテクチャは Uncle Bob が提唱したもので(2012)、UseCase と Entity の分離を重視します。ヘキサゴナルアーキテクチャは「対称性」(プライマリー/セカンダリーアダプター)に重点を置き、クリーンアーキテクチャは「依存関係のルール」に重点を置きます。
Q2: ポートは Domain 層と Application 層のどちらに置くべきですか? A: Domain 層に置くことをおすすめします。Domain はビジネスの中核であり、ポートはビジネスが外部依存を抽象化したものです。
Q3: ポートの過度な抽象化をどうすれば避けられますか? A: YAGNI 原則に従い、現時点で確実な変更要件に対してのみポートを定義します。
Q4: UseCase インターフェースにはメソッドを 1 つだけ定義すべきですか、それとも複数定義してもよいですか? A: インターフェースごとにメソッドを 1 つにすることをおすすめします(インターフェース分離の原則)。ただし、OrderRepository の save/findById のように高度に関連するメソッドは、同じインターフェースにまとめてもかまいません。
Q5: Controller 層の DTO と Domain 層の Entity を共用できますか? A: できません。DTO はアダプター層のデータ運搬体であり、Entity はドメイン層のビジネスモデルです。変換には Mapper が必要です。
Q6: ヘキサゴナルアーキテクチャではクエリをどう扱いますか? A: クエリもポート経由で処理します。QueryOrderUseCase を使用して読み取り専用 DTO を返し、複雑なクエリには CQRS を使用できます。
Q7: ヘキサゴナルアーキテクチャの境界が正しいかどうか、どう検証できますか? A: データベースと HTTP を起動せずに、Domain 層の単体テストをすべて実行できるでしょうか?できる → 正しい。できない → 依存が漏れています。
Q8: チームがヘキサゴナルアーキテクチャに慣れていない場合はどうすればよいですか? A: 段階的に導入してください。まず Domain 層の純粋性とポートの定義を整え、その後、徐々にアダプターを抽出します。
参考資料
| ファイル | 目的 |
|---|---|
| references/01-port-definitions.md | ポート定義の規約 — 入出力ポート、命名、粒度、Strong/Weak |
| references/02-primary-adapters.md | プライマリアダプターの詳細 — REST/CLI/gRPC/MQ の 4 種類のアダプター実装 |
| references/03-secondary-adapters.md | セカンダリアダプターの詳細 — JPA/MyBatis/Stripe/RabbitMQ/InMemory |
| references/04-domain-model.md | ドメインモデルの設計 — 集約ルート、エンティティ、値オブジェクト、ドメインサービス、ドメインイベント |
| references/05-application-services.md | アプリケーションサービス層 — UseCase の実装、オーケストレーション規約、コマンドオブジェクト |
| references/06-di-configuration.md | DI 設定 — Spring Config、Profile の切り替え、複数環境のアダプター |
| references/07-testing.md | テスト戦略 — ドメイン層/アプリケーション層/アダプター/アーキテクチャの 4 段階テスト |
| references/08-migration.md | 段階的な移行 — 従来の 3 層構造からヘキサゴナルアーキテクチャへの移行ガイド |
| examples/01-order-hexagonal-complete.md | 注文のヘキサゴナルアーキテクチャ完全版の例 — Domain/Port/Service/Adapter の全フロー |
| examples/02-user-registration-example.md | ユーザー登録の例 — 値オブジェクト、検証コード、ドメインイベント |
| examples/03-multi-entry-points-example.md | 複数エントリーポイントのシステム例 — 同じ UseCase を REST/CLI/Kafka/gRPC から呼び出す |
| examples/04-adapter-swapping-example.md | アダプターの差し替え可能性の例 — Postgres→MongoDB、Stripe→PayPal をコード変更なしで実現 |
| examples/05-port-swapping-test.md | プライマリアダプターの差し替え可能性の例 — REST/gRPC/CLI の 3 種類のエントリーポイント + ポートレベルのテスト |
| examples/06-monolith-simple.md | シンプルなモノリス型ヘキサゴナルアーキテクチャ — 単一モジュールの 4 パッケージ + ports/adapters サブパッケージ構造 |
| examples/07-monolith-complex.md | 複雑なモノリス型ヘキサゴナルアーキテクチャ — 複数のポート + 複数のアダプター + 複数の集約ルート |
| examples/08-monolith-multi-module.md | マルチモジュールのモノリス型ヘキサゴナルアーキテクチャ — Maven マルチモジュールによるコンパイル時の境界制約 |
| examples/09-microservice-simple.md | シンプルなマイクロサービス型ヘキサゴナルアーキテクチャ — 単一マイクロサービス内の最小限のヘキサゴナル構造 |
| examples/10-microservice-complex.md | 複雑なマイクロサービス型ヘキサゴナルアーキテクチャ — 複数の集約 + Saga + サービス間呼び出し |
| examples/11-microservice-multi-module.md | マルチモジュールのマイクロサービス型ヘキサゴナルアーキテクチャ — 各サービス内の Maven マルチモジュール |
| examples/12-microservice-complex-multi.md | 複雑なマイクロサービスのマルチモジュール構成 — CQRS + 読み書き分離 + CDC + 分散 Saga |
主な資料
- Hexagonal Architecture — Alistair Cockburn (2005)
- Hexagonal Architecture Explained — Cockburn & Garrido de Paz (2024)
- Domain-Driven Design: The Blue Book — Eric Evans (2003)
- AWS: Hexagonal Architecture Pattern
ナビゲーション
- → 次へ:
ddd-domain-designerスキルに引き継ぎます — ヘキサゴナルアーキテクチャ向けのドメインモデルを設計します。インストール:npx skills add full-stack-skills/ddd-skills --skill ddd-domain-designer. - 🔗 関連:
ddd-testing-strategist— ポートの Mock テスト(インストール:npx skills add full-stack-skills/ddd-skills --skill ddd-testing-strategist) |ddd-api-designer— ヘキサゴナル API の設計(インストール:npx skills add full-stack-skills/ddd-skills --skill ddd-api-designer)。
💡 ヘキサゴナルアーキテクチャ = ポート + アダプター。検証方法:データベースや HTTP を起動せず、CLI/単体テストだけでビジネスロジックを実行できれば、境界は適切です。