ポートとアダプター
アプリケーションのコアロジックを外部技術から分離し、ポート (インターフェース) とアダプター (実装) で接続するアーキテクチャ
ポートとアダプターとは
ポートとアダプター (Ports and Adapters) は、Alistair Cockburn が 2005 年の技術レポートで示した設計パターンで、アプリケーションのコアロジック (ドメイン) を外部技術 (DB、UI、外部 API) から分離する。原典ではこの「ポートとアダプター」がパターン名で、ヘキサゴナルアーキテクチャ (Hexagonal Architecture) は図の見た目に由来する別名として併記されている。日本語圏では別名の方が先に広まったため主従が逆に見えるが、原典を読むときは正式名がこちらだと押さえておくと迷わない。原典は現在も本人のサイトで公開されている (2026 年 8 月時点)。
- ポート: アプリケーションが外部と通信するためのインターフェース
- アダプター: ポートの具体的な実装 (DynamoDB アダプター、SES アダプター)
コアロジックは「DynamoDB に保存する」ではなく「OrderRepository に保存する」と記述する。DynamoDB か PostgreSQL かはアダプターが決める。
構造
原典の呼称は一次 (primary) ポート・アダプターと二次 (secondary) ポート・アダプターで、駆動側 (driving) / 被駆動側 (driven) はその別称として示されている。区別の基準は「会話を始めるのがどちら側か」で、ユースケースの主アクター・副アクターの考え方に対応する。以下では日本語で通りの良い駆動側 / 被駆動側の呼び方で説明する。
駆動側ポート (Driving Port) は外部からアプリケーションコアを呼び出すインターフェースで、HTTP コントローラーや Lambda ハンドラがアダプターとして接続する。被駆動側ポート (Driven Port) はコアが外部リソースにアクセスするインターフェースで、DynamoDB や SQS のアダプターが接続する。コアはポートのインターフェースのみに依存し、具体的なアダプターを知らない。
図が六角形なのは 6 という数に意味があるからではなく、階層図の一次元的な並びから離れて必要な数のポートとアダプターを書き込めるようにするためだ。原典は実際のポート数を 2〜4 個としている (著者が見た最多が 4)。
[HTTP Controller] [Lambda Handler] [CLI]
↓ ↓ ↓
─── 駆動側ポート (Driving Port) ───
│ │
▼ ▼
┌─────────────────────────────────────────────┐
│ アプリケーションコア │
│ ┌─────────────────────────────────────┐ │
│ │ ドメインロジック │ │
│ │ OrderService, PricingPolicy │ │
│ └─────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
│ │
─── 被駆動側ポート (Driven Port) ───
↓ ↓ ↓
[DynamoDB Adapter] [SES Adapter] [Stripe Adapter]
TypeScript での実装
TypeScript での実装のコード例を示す。
// ドメイン (外部技術を知らない)
interface Order {
readonly id: string;
readonly userId: string;
complete(): void;
}
// ポート (アプリケーション側が定義)
interface OrderRepository {
findById(id: string): Promise<Order | null>;
save(order: Order): Promise<void>;
}
interface NotificationPort {
send(userId: string, message: string): Promise<void>;
}
// アプリケーションコア (外部技術に依存しない)
class OrderService {
constructor(
private readonly repo: OrderRepository,
private readonly notifier: NotificationPort,
) {}
async completeOrder(orderId: string): Promise<void> {
const order = await this.repo.findById(orderId);
if (!order) throw new Error('Order not found');
order.complete();
await this.repo.save(order);
await this.notifier.send(order.userId, `Order ${orderId} completed`);
}
}
// アダプター (インフラ側が実装・SDK の import は省略)
class DynamoDBOrderRepo implements OrderRepository {
constructor(private readonly client: DynamoDBDocumentClient) {}
async findById(id: string): Promise<Order | null> {
const res = await this.client.send(new GetCommand({ TableName: 'orders', Key: { id } }));
return (res.Item as Order | undefined) ?? null;
}
async save(order: Order): Promise<void> {
await this.client.send(new PutCommand({ TableName: 'orders', Item: order }));
}
}
class SESNotificationAdapter implements NotificationPort {
constructor(private readonly client: SESClient) {}
async send(userId: string, message: string): Promise<void> {
/* this.client.send(new SendEmailCommand(...)) でメール送信 */
}
}
// 組み立て (Composition Root)
const service = new OrderService(
new DynamoDBOrderRepo(ddbClient),
new SESNotificationAdapter(sesClient),
);
駆動側と被駆動側
駆動側と被駆動側を以下にまとめる。
| 種類 | 方向 | 例 |
|---|---|---|
| 駆動側 (Driving・原典では primary) | 外部 → アプリ | HTTP Controller, Lambda Handler, CLI, テスト |
| 被駆動側 (Driven・原典では secondary) | アプリ → 外部 | DB, メール, 外部 API, ファイルシステム |
駆動側アダプターはアプリケーションを「呼び出す」側、被駆動側アダプターはアプリケーションから「呼び出される」側だ。
テスタビリティ
最大のメリットはテスト時にアダプターを差し替えられること。
// テスト: インメモリアダプターを注入
const mockRepo: OrderRepository = {
findById: vi.fn().mockResolvedValue({ id: '1', userId: 'u1', complete: vi.fn() }),
save: vi.fn(),
};
const mockNotifier: NotificationPort = { send: vi.fn() };
const service = new OrderService(mockRepo, mockNotifier);
await service.completeOrder('1');
expect(mockRepo.save).toHaveBeenCalled();
expect(mockNotifier.send).toHaveBeenCalledWith('u1', expect.any(String));
DB 接続もメール送信も不要なので、テストはプロセス内で完結し、実行時間は実インフラを叩く場合より桁違いに短い。速さ以上に効くのは、失敗したときに疑う場所がコアの中だけに絞られることだ。
Clean Architecture との関係
Clean Architecture (Robert C. Martin) は 2012 年の記事で、ヘキサゴナル (ポートとアダプター)・オニオンアーキテクチャ・DCI・BCE といった先行案を「目的は同じ」と整理し、1 枚の同心円図に統合したものだ。ポートとアダプターの直系の後継ではなく、同じ狙いを持つ複数の案の共通項を言い直した位置づけになる。共通の核は「依存の方向を内側 (ドメイン) に向ける」ことで、Clean Architecture はそこにレイヤーの細分と依存性逆転の作法を足している。
ポートとアダプターの対応
ポートとアダプターの対応を以下に整理する。
| ポート (インターフェース) | アダプター (実装) |
|---|---|
| OrderRepository | DynamoDBOrderRepository |
| NotificationService | SESNotificationService |
| PaymentGateway | StripePaymentGateway |
| OrderRepository (テスト) | InMemoryOrderRepository |
現場での応用を知るには関連書籍も役立つ。
この記事は役に立ちましたか?
関連用語
ヘキサゴナルアーキテクチャ
ポートとアダプターでビジネスロジックを外部依存から分離する設計パターン
クリーンアーキテクチャ
ビジネスロジックを外部の技術的詳細から分離し、依存関係を内側に向けることで変更に強い設計を実現するアーキテクチャ原則
依存性逆転の原則
SOLID の D - 上位モジュールは下位モジュールに依存せず、両者とも抽象に依存すべきという設計原則
ポート
ネットワーク通信でプロセスを識別する番号で、TCP/UDP の接続先を指定する
アダプターパターン
互換性のないインターフェースを変換し、既存のコードを変更せずに連携させるデザインパターン
オニオンアーキテクチャ
ドメインロジックを中心に据え、外側の層が内側に依存する同心円状のアーキテクチャ
関連する記事
IT パスポート参考書の選び方 - 完読できる 1 冊と過去問の組み合わせ
IT パスポート試験の参考書選びを「完読できるか」を軸に整理。イラスト型 / 絞り込み型 / 丁寧解説型などテキストの個性の見分け方、過去問題集との組み合わせ、年度版とシラバス対応の注意点を 2026 年 8 月時点の定番書で解説します。
あなたの「積ん読」は投資ポートフォリオである
積ん読を罪悪感の対象ではなく、知識への分散投資として捉え直す視点を提案します。積ん読の戦略的な管理方法と、必要なときに引き出す仕組みづくり。
本についてくるダウンロード素材を使い倒す
プログラミングの本には、サンプルコードや練習用データのダウンロード特典がついていることがあります。素材の探し方と、完成版コードの使い方を解説します。