ポート&アダプター

英語名 Ports And Adapters
読み方 ポーツ アンド アダプターズ
難易度
所要時間 30分〜1時間
提唱者 Alistair Cockburn (2005) / Hexagonal Architecture
目次

ひとことで言うと
#

アプリケーションの中核ロジックを「ポート(インターフェース)」で囲み、外部との接続を「アダプター」で実装することで、DB・API・UIなどの外部依存を自由に差し替え可能にする設計パターン。別名ヘキサゴナルアーキテクチャ。

押さえておきたい用語
#

押さえておきたい用語
Port(ポート)
アプリケーションの境界に定義するインターフェースのこと。外部とのやり取りの契約を規定する。
Adapter(アダプター)
ポートの実装。特定の技術でポートの契約を満たすコンポーネントを指す。
Driving Adapter(駆動側アダプター)
アプリケーションを呼び出す側のアダプター。HTTP Controller、CLIコマンド、メッセージコンシューマーなど。
Driven Adapter(被駆動側アダプター)
アプリケーションから呼び出される側のアダプター。DBリポジトリ、外部APIクライアント、メール送信など。
Hexagonal Architecture(ヘキサゴナル アーキテクチャ)
Ports & Adaptersの別名。六角形の図でアプリケーションの中核を囲む形で表現するためこう呼ばれる。

ポート&アダプターの全体像
#

Ports & Adapters:中核ロジックをポートで囲みアダプターで接続する
Application Coreドメインロジックユースケース外部技術に依存しないDriving(駆動側)HTTP ControllerCLI CommandEvent ConsumerInput PortDriven(被駆動側)DB RepositoryExternal APIEmail SenderOutput Portポート = 契約(インターフェース) / アダプター = 実装(技術固有)テスト時はアダプターをモック/スタブに差し替えるだけDB変更やAPI移行もアダプターの差し替えで完結する
Ports & Adapters適用の進め方フロー
1
コア定義
ドメインロジックとユースケースを特定する
2
ポート設計
Input/Outputのインターフェースを定義する
3
アダプター実装
DB・API・UI等の技術固有の実装を作る
テストで検証
アダプターをモックに差し替えてコアをテスト

こんな悩みに効く
#

  • 外部APIの仕様変更でビジネスロジックまで修正が必要になる
  • DBを変更したい(MySQL→PostgreSQL等)が影響範囲が大きすぎて踏み切れない
  • ドメインロジックのテストに外部サービスのモック設定が大量に必要

基本の使い方
#

アプリケーションの中核をポートで囲む
ビジネスロジックとユースケースを「Application Core」として定義し、外部とのやり取りをすべてポート(インターフェース)経由にする。Coreは技術固有のライブラリをインポートしない。
Driving / Drivenの2種類でアダプターを分類する
Driving Adapter(アプリを呼び出す側): HTTP Controller、GraphQL Resolver、CLI、メッセージコンシューマー。Driven Adapter(アプリから呼び出される側): DBリポジトリ、外部APIクライアント、メール送信、ファイルストレージ。
テスト時にアダプターを差し替えて高速にフィードバックを得る
Driven Adapterをインメモリ実装やモックに差し替えることで、外部サービスなしでドメインロジックをテストできる。Driving Adapterのテストは統合テストで実施する。

具体例
#

例1:EC企業が決済プロバイダーの差し替えを1日で完了する

月間5億円のEC。決済プロバイダーの手数料値上げに伴い、Stripe→別プロバイダーへの移行が必要になった。しかし決済ロジックがコントローラーに直接書かれており、移行見積もりは 3ヶ月

まずPorts & Adaptersで決済周りをリファクタリング。PaymentPortインターフェースを定義し、StripePaymentAdapterとして既存実装を切り出した。

新プロバイダーのNewPaymentAdapterを実装し、DIコンテナの設定を1行変更するだけで切り替え完了。テスト環境での検証を含め、切り替え作業は 1日 で完了。手数料削減効果は年間 1,800万円

例2:SaaS企業がDBをMySQLからPostgreSQLに移行する

エンジニア40名のBtoB SaaS。MySQLの全文検索性能が限界に達し、PostgreSQLへの移行を検討。しかしSQLが全レイヤーに散在しており、影響範囲の特定に 2週間 かかる見込みだった。

段階的にPorts & Adaptersを導入。Repositoryインターフェース(Port)を定義し、既存のMySQL実装をAdapterとして整理。その後PostgreSQL Adapterを新規実装。

Feature Flagで一部テナントからPostgreSQLに切り替え、問題がないことを確認してから全テナントに展開。移行期間は 6週間 で、ビジネスロジックのコードは 1行も変更なし

例3:IoT企業がメッセージブローカーを柔軟に切り替える

IoTプラットフォームを開発するエンジニア20名の企業。顧客ごとにAWS環境(SQS)とAzure環境(Service Bus)のどちらかにデプロイする必要があり、メッセージング部分のコードが2重管理されていた。

MessageBrokerPortを定義し、SQSAdapterServiceBusAdapterを実装。デプロイ先の環境変数でアダプターを自動選択する設計にした。

コードの2重管理が解消され、新機能追加時の工数が 1.5倍 → 1倍 に。さらにGCP(Pub/Sub)対応も PubSubAdapter を追加するだけで実現。新規クラウド対応の工数が 3週間 → 3日 に短縮された。

やりがちな失敗パターン
#

  1. すべての外部接続にポートを作る — ログライブラリや設定ファイル読み取りまでポート化するのはやりすぎ。差し替えの可能性がある依存だけにポートを定義する
  2. ポートの粒度が粗すぎる — 1つのポートにCRUD全操作を詰め込むと、テスト時のモックが複雑になる。ユースケース単位でポートを分ける
  3. アダプターにビジネスロジックが漏れる — DB Adapterの中で複雑なSQL条件分岐を書くと、ドメインロジックがAdapterに分散する。条件判定はCoreで行い、Adapterはデータの永続化に徹する
  4. テストでアダプターを差し替えない — Ports & Adaptersの最大のメリットはテスタビリティ。テストで実際にモック/スタブを活用しないと投資対効果が出ない

よくある質問
#

Q: ポート(Port)とインターフェース(interface)は同じものですか? A: ポートはアーキテクチャ上の概念で、アプリケーションコアが外部と通信するための「窓口」を指します。実装上はインターフェース(JavaやTypeScriptのinterface、Goのinterface{})として表現されることが多いです。「ポート」はアーキテクチャ上の役割を、「インターフェース」はその実装手段を指すと区別して理解するとよいでしょう。

Q: テスト時にアダプターをどうやって差し替えますか? A: テスト環境ではDI(依存性注入)コンテナや手動でのコンストラクタ注入で、本番のDBアダプターの代わりにインメモリのモックアダプターを注入します。例えばUserRepositoryポートに対して、本番はPostgresAdapter、テストはInMemoryUserRepositoryを渡すことで、DBなしでドメインロジックをテストできます。

Q: レイヤードアーキテクチャとヘキサゴナルアーキテクチャの違いは何ですか? A: レイヤードアーキテクチャは上から下への一方向の依存(UI→Application→Domain→Infrastructure)を定義しますが、Infrastructureが最下層にあるため依存関係逆転を明示しにくいです。ヘキサゴナルは中心にCoreを置き外側をアダプターとして扱うことで、依存がすべてCoreに向かうことを構造的に強制します。DIPがより徹底されています。

Q: すべての外部依存にポートを作るべきですか? A: 必要ありません。差し替えの可能性がある依存(DBやメッセージキューなどのインフラ、外部サービスAPI)にポートを定義してください。ロギングライブラリや設定ファイル読み取りなど差し替えをほぼ想定しないものまでポート化すると過設計になります。「本番とテストで別物を使いたいか?」が判断軸になります。

まとめ
#

Ports & Adapters(ヘキサゴナルアーキテクチャ)はアプリケーションの中核をポートで囲み、外部依存をアダプターとして実装する設計パターン。Driving(呼び出す側)とDriven(呼び出される側)の2種類のアダプターで外部接続を整理し、テスト時はアダプターの差し替えだけでドメインロジックを検証できる。DB移行・プロバイダー変更・マルチクラウド対応といった技術選定の変更に強いアーキテクチャを実現する。