Communitygithub.com

ヘキサゴナルアーキテクチャ

TypeScript、Java、Kotlin、Goサービスにおいて、明確なドメイン境界、依存性逆転、テスト可能なユースケースオーケストレーションを備えたPorts & Adaptersシステムを設計、実装、リファクタリングします。Ports and Adaptersの導入やリファクタリング、またはドメインロジックがI/Oに絡まっている場合に使用します。

ヘキサゴナルアーキテクチャ とは?

Hexagonal Architecture(ヘキサゴナルアーキテクチャ / Ports and Adapters)は、ビジネスロジックをフレームワークやトランスポート、永続化の詳細から独立させるアーキテクチャパターンです。コアとなるアプリケーションは抽象的なポートに依存し、アダプターがそのポートを境界で実装します。このスキルは、長期保守性とテスト容易性が求められる新機能の構築や、レイヤードアーキテクチャやフレームワークに依存したコードでドメインロジックがI/Oと混在している場合のリファクタリングに最適です。また、同一ユースケースに対してHTTP、CLI、キュー、cronジョブなど複数のインターフェースをサポートする場合や、データベース、外部API、メッセージバスなどのインフラをビジネスルールを書き換えずに置き換える場合にも役立ちます。TypeScript、Java、Kotlin、Goなど多様な言語に対応しており、各言語の構文やスタイルに合わせた具体的な実装例とモジュール構成を提供します。依存関係は常に内側(アダプター→アプリケーション/ドメイン、アプリケーション→ポートインターフェース、ドメイン→外部には依存しない)に向かうことで、クリーンな依存関係の方向性を維持します。

対応~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/hexagonal-architecture

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

ヘキサゴナルアーキテクチャ は何をしますか?

ヘキサゴナルアーキテクチャ(Ports and Adapters)は、ビジネスロジックをフレームワーク、トランスポート、永続化の詳細から独立させます。コアアプリケーションは抽象的なポートに依存し、アダプターがそのポートを境界で実装します。

使用すべき場面

  • 長期的な保守性とテスト容易性が重要な新機能の構築時。
  • レイヤードアーキテクチャやフレームワークに依存したコードで、ドメインロジックがI/Oと混在している場合のリファクタリング時。
  • 同じユースケースに対して複数のインターフェース(HTTP、CLI、キュー、cronジョブ)をサポートする場合。
  • ビジネスルールを書き換えずにインフラストラクチャ(データベース、外部API、メッセージバス)を置き換える場合。

リクエストに境界、ドメイン中心設計、密結合サービスのリファクタリング、または特定のライブラリからのアプリケーションロジックの分離が含まれる場合にこのスキルを使用してください。

コアコンセプト

  • ドメインモデル: ビジネスルールとエンティティ/値オブジェクト。フレームワークのインポートはありません。
  • ユースケース(アプリケーション層): ドメインの動作とワークフローのステップをオーケストレーションします。
  • インバウンドポート: アプリケーションが何をできるかを記述する契約(コマンド/クエリ/ユースケースインターフェース)。
  • アウトバウンドポート: アプリケーションが必要とする依存関係の契約(リポジトリ、ゲートウェイ、イベントパブリッシャー、クロック、UUIDなど)。
  • アダプター: ポートのインフラストラクチャおよび配信実装(HTTPコントローラー、DBリポジトリ、キューコンシューマー、SDKラッパー)。
  • コンポジションルート: 具体的なアダプターをユースケースにバインドする単一の配線場所。

アウトバウンドポートインターフェースは通常、アプリケーション層(または抽象化が真にドメインレベルである場合のみドメイン層)に配置され、インフラストラクチャアダプターがそれらを実装します。

依存関係の方向は常に内側へ向かいます。

  • アダプター -> アプリケーション/ドメイン
  • アプリケーション -> ポートインターフェース(インバウンド/アウトバウンド契約)
  • ドメイン -> ドメインのみの抽象化(フレームワークやインフラストラクチャへの依存なし)
  • ドメイン -> 外部への依存なし

動作方法

ステップ 1: ユースケース境界をモデル化する

明確な入力と出力DTOを持つ単一のユースケースを定義します。トランスポートの詳細(Expressのreq、GraphQLのcontext、ジョブペイロードラッパー)はこの境界の外側に保持します。

ステップ 2: 最初にアウトバウンドポートを定義する

すべての副作用をポートとして特定します。

  • 永続化(UserRepositoryPort)
  • 外部呼び出し(BillingGatewayPort)
  • 横断的関心事(LoggerPort、ClockPort)

ポートは機能(capabilities)をモデル化するものであり、技術(technologies)をモデル化するものではありません。

ステップ 3: 純粋なオーケストレーションでユースケースを実装する

ユースケースのクラス/関数は、コンストラクタまたは引数を介してポートを受け取ります。アプリケーションレベルの不変条件を検証し、ドメインルールを調整し、プレーンなデータ構造を返します。

ステップ 4: 境界にアダプターを構築する

  • インバウンドアダプターは、プロトコル入力をユースケース入力に変換します。
  • アウトバウンドアダプターは、アプリケーション契約を具体的なAPI/ORM/クエリビルダーにマッピングします。
  • マッピングはアダプター内に留まり、ユースケースの内部には入りません。

ステップ 5: コンポジションルートですべてを配線する

アダプターをインスタンス化し、それらをユースケースに注入します。この配線を一元化して、隠れたサービスロケータの動作を回避します。

ステップ 6: 境界ごとにテストする

  • 偽のポートを使用してユースケースを単体テストします。
  • 実際のインフラ依存関係を使用してアダプターの統合テストを実行します。
  • インバウンドアダプターを通じてユーザー向けフローのE2Eテストを実行します。

アーキテクチャ図

flowchart LR
  Client["クライアント (HTTP/CLI/Worker)"] --> InboundAdapter["インバウンドアダプター"]
  InboundAdapter -->|"呼び出し"| UseCase["ユースケース (アプリケーション層)"]
  UseCase -->|"使用"| OutboundPort["アウトバウンドポート (インターフェース)"]
  OutboundAdapter["アウトバウンドアダプター"] -->|"実装"| OutboundPort
  OutboundAdapter --> ExternalSystem["DB/API/キュー"]
  UseCase --> DomainModel["ドメインモデル"]

推奨モジュール構成

明示的な境界を持つ機能優先の構成を使用します。

src/
  features/
    orders/
      domain/
        Order.ts
        OrderPolicy.ts
      application/
        ports/
          inbound/
            CreateOrder.ts
          outbound/
            OrderRepositoryPort.ts
            PaymentGatewayPort.ts
        use-cases/
          CreateOrderUseCase.ts
      adapters/
        inbound/
          http/
            createOrderRoute.ts
        outbound/
          postgres/
            PostgresOrderRepository.ts
          stripe/
            StripePaymentGateway.ts
      composition/
        ordersContainer.ts

TypeScript の例

ポート定義

export interface OrderRepositoryPort {
  save(order: Order): Promise<void>;
  findById(orderId: string): Promise<Order | null>;
}

export interface PaymentGatewayPort {
  authorize(input: { orderId: string; amountCents: number }): Promise<{ authorizationId: string }>;
}

ユースケース

type CreateOrderInput = {
  orderId: string;
  amountCents: number;
};

type CreateOrderOutput = {
  orderId: string;
  authorizationId: string;
};

export class CreateOrderUseCase {
  constructor(
    private readonly orderRepository: OrderRepositoryPort,
    private readonly paymentGateway: PaymentGatewayPort
  ) {}

  async execute(input: CreateOrderInput): Promise<CreateOrderOutput> {
    const order = Order.create({ id: input.orderId, amountCents: input.amountCents });

    const auth = await this.paymentGateway.authorize({
      orderId: order.id,
      amountCents: order.amountCents,
    });

    // markAuthorized は新しい Order インスタンスを返します。その場で変更するわけではありません。
    const authorizedOrder = order.markAuthorized(auth.authorizationId);
    await this.orderRepository.save(authorizedOrder);

    return {
      orderId: order.id,
      authorizationId: auth.authorizationId,
    };
  }
}

アウトバウンドアダプター

export class PostgresOrderRepository implements OrderRepositoryPort {
  constructor(private readonly db: SqlClient) {}

  async save(order: Order): Promise<void> {
    await this.db.query(
      "insert into orders (id, amount_cents, status, authorization_id) values ($1, $2, $3, $4)",
      [order.id, order.amountCents, order.status, order.authorizationId]
    );
  }

  async findById(orderId: string): Promise<Order | null> {
    const row = await this.db.oneOrNone("select * from orders where id = $1", [orderId]);
    return row ? Order.rehydrate(row) : null;
  }
}

コンポジションルート

export const buildCreateOrderUseCase = (deps: { db: SqlClient; stripe: StripeClient }) => {
  const orderRepository = new PostgresOrderRepository(deps.db);
  const paymentGateway = new StripePaymentGateway(deps.stripe);

  return new CreateOrderUseCase(orderRepository, paymentGateway);
};

複数言語マッピング

エコシステム全体で同じ境界ルールを使用します。変更されるのは構文と配線スタイルのみです。

  • TypeScript/JavaScript
    • ポート: application/ports/* のインターフェース/型として。
    • ユースケース: コンストラクタ/引数注入を持つクラス/関数。
    • アダプター: adapters/inbound/*、adapters/outbound/*。
    • コンポジション: 明示的なファクトリ/コンテナモジュール(隠れたグローバル変数はなし)。
  • Java
    • パッケージ: domain、application.port.in、application.port.out、application.usecase、adapter.in、adapter.out。
    • ポート: application.port.* のインターフェース。
    • ユースケース: プレーンクラス(Springの @Service はオプションであり必須ではありません)。
    • コンポジション: Spring設定または手動の配線クラス。配線はドメイン/ユースケースクラスの外に保持します。
  • Kotlin
    • モジュール/パッケージはJavaの分割(domain、application.port、application.usecase、adapter)を反映します。
    • ポート: Kotlinインターフェース。
    • ユースケース: コンストラクタ注入を持つクラス(Koin/Dagger/Spring/手動)。
    • コンポジション: モジュール定義または専用のコンポジション関数。サービスロケータパターンは避けます。
  • Go
    • パッケージ: internal/<feature>/domain、application、ports、adapters/inbound、adapters/outbound。
    • ポート: 利用側のアプリケーションパッケージが所有する小さなインターフェース。
    • ユースケース: インターフェースフィールドと明示的な New... コンストラクタを持つ構造体。
    • コンポジション: cmd/<app>/main.go(または専用の配線パッケージ)で配線。コンストラクタは明示的に保ちます。

避けるべきアンチパターン

  • ドメインエンティティがORMモデル、Webフレームワーク型、またはSDKクライアントをインポートすること。
  • ユースケースが req、res、またはキューメタデータを直接読み取ること。
  • ユースケースからドメイン/アプリケーションのマッピングなしにデータベース行を直接返すこと。
  • アダプターがユースケースのポートを経由せずに互いに直接呼び出しを行うこと。
  • 依存関係の配線を多数のファイルに分散させ、隠れたグローバルシングルトンを使用すること。

移行プレイブック

  1. 変更頻度が高く、痛みを伴う1つの垂直スライス(単一のエンドポイント/ジョブ)を選択します。
  2. 明示的な入力/出力型を持つユースケース境界を抽出します。
  3. 既存のインフラストラクチャ呼び出しの周りにアウトバウンドポートを導入します。
  4. オーケストレーションロジックをコントローラー/サービスからユースケースに移動します。
  5. 古いアダプターはそのままにしますが、新しいユースケースに委譲するようにします。
  6. 新しい境界の周りにテスト(単体 + アダプター統合)を追加します。
  7. スライスごとに繰り返します。全面書き換えは避けます。

既存システムのリファクタリング

  • ストラングラーパターン: 現在のエンドポイントを維持し、新しいポート/アダプターを通じて一度に1つのユースケースをルーティングします。
  • ビッグバン書き換えはしない: 機能スライスごとに移行し、特性テストで動作を保持します。
  • まずはファサード: 内部を置き換える前に、レガシーサービスをアウトバウンドポートの背後にラップします。
  • コンポジションの凍結: 新しい依存関係がドメイン/ユースケース層に漏れ出さないように、早期に配線を一元化します。
  • スライス選択ルール: 変更が多く、影響範囲が小さいフローを優先します。
  • ロールバックパス: 本番動作が検証されるまで、移行されたスライスごとに可逆的なトグルまたはルートスイッチを維持します。

テストガイダンス(同じヘキサゴナル境界)

  • ドメインテスト: エンティティ/値オブジェクトを純粋なビジネスルールとしてテストします(モックなし、フレームワーク設定なし)。
  • ユースケース単体テスト: アウトバウンドポートのフェイク/スタブを使用してオーケストレーションをテストします。ビジネス成果とポートの相互作用をアサートします。
  • アウトバウンドアダプター契約テスト: ポートレベルで共有契約スイートを定義し、各アダプター実装に対して実行します。
  • インバウンドアダプターテスト: プロトコルマッピング(HTTP/CLI/キューペイロードからユースケース入力、出力/エラーのプロトコルへのマッピング)を検証します。
  • アダプター統合テスト: 実際のインフラストラクチャ(DB/API/キュー)に対して、シリアライズ、スキーマ/クエリ動作、リトライ、タイムアウトをテストします。
  • エンドツーエンドテスト: インバウンドアダプター -> ユースケース -> アウトバウンドアダプターを通じて重要なユーザージャーニーをカバーします。
  • リファクタリングの安全性: 抽出前に特性テストを追加します。新しい境界の動作が安定し、同等になるまでテストを保持します。

ベストプラクティスチェックリスト

  • ドメイン層とユースケース層は、内部の型とポートのみをインポートする。
  • すべての外部依存関係は、アウトバウンドポートによって表現される。
  • 検証は境界(インバウンドアダプター + ユースケースの不変条件)で行う。
  • 不変の変換を使用する(共有状態を変更する代わりに、新しい値/エンティティを返す)。
  • エラーは境界を越えて変換される(インフラエラー -> アプリケーション/ドメインエラー)。
  • コンポジションルートは明示的で監査しやすい。
  • ユースケースは、ポートのシンプルなインメモリフェイクでテスト可能である。
  • リファクタリングは、動作を保持するテストを持つ1つの垂直スライスから開始する。
  • 言語/フレームワーク固有の詳細はアダプターに留め、ドメインルールには決して入れない。

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

関連スキル