Communitygithub.com

六角架構

設計、實作與重構 Ports & Adapters 系統,在 TypeScript、Java、Kotlin 和 Go 服務中,保持清晰的領域邊界、依賴反轉與可測試的用例編排。適合在導入或重構成 Ports and Adapters,或當領域邏輯與 I/O 糾纏不清時使用。

六角架構 是什麼?

六角架構(Ports and Adapters)是一種將業務邏輯與框架、傳輸及持久化細節解耦的軟體設計模式。它的核心在於讓應用程式依賴於抽象的埠,並由邊緣的適配器來實作這些埠。此技能定義了完整的建構步驟,從用例邊界建模、定義輸出埠、編排用例邏輯,到建構適配器並在組合根中集中注入所有依賴。技能涵蓋 TypeScript、Java、Kotlin 與 Go 等語言的實作對應,並提供反模式清單、漸進式遷移手冊(如扼殺者模式)以及跨邊界的測試策略,包含領域測試、單元測試、適配器合約測試與端到端測試。開發者能在不重寫整個系統的情況下,逐步引入六角架構,從而提升長期的可維護性與可測試性。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/ECC/tree/main/skills/hexagonal-architecture

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

六角架構 是做什麼的?

六角架構(Ports and Adapters)讓業務邏輯獨立於框架、傳輸及持久化細節。核心應用程式依賴於抽象的埠,而邊緣的適配器則實作這些埠。

使用時機

  • 在開發需要長期維護性與可測試性的新功能時。
  • 重構層級式或框架重度程式碼,當領域邏輯與 I/O 問題混合在一起時。
  • 支援同一用例的多種介面(HTTP、CLI、佇列工作者、cron 任務)。
  • 更換基礎設施(資料庫、外部 API、訊息匯流排)時,無需重寫業務規則。

當請求涉及邊界、領域驅動設計、重構緊密耦合服務,或將應用邏輯與特定函式庫解耦時,請使用此技能。

核心概念

  • 領域模型:業務規則與實體/值物件。不包含框架匯入。
  • 用例(應用層):編排領域行為與工作流程步驟。
  • 輸入埠:描述應用程式能做什麼的合約(命令/查詢/用例介面)。
  • 輸出埠:應用程式所需依賴的合約(儲存庫、閘道器、事件發佈者、時鐘、UUID 等)。
  • 適配器:輸入與輸出埠的基礎設施與交付實作(HTTP 控制器、資料庫儲存庫、佇列消費者、SDK 包裝器)。
  • 組合根:將具體適配器繫結到用例的單一注入位置。

輸出埠介面通常位於應用層(或僅當抽象確實是領域層級時才放在領域層),而基礎設施的適配器則實作它們。

依賴方向永遠向內:

  • 適配器 -> 應用程式/領域
  • 應用程式 -> 埠介面(輸入/輸出合約)
  • 領域 -> 僅限領域的抽象(無框架或基礎設施依賴)
  • 領域 -> 無外部依賴

運作方式

步驟 1:建模用例邊界

定義單一用例,包含清晰的輸入與輸出 DTO。將傳輸細節(Express req、GraphQL context、任務酬載包裝器)保留在此邊界之外。

步驟 2:先定義輸出埠

將每個副作用識別為一個埠:

  • 持久化(UserRepositoryPort)
  • 外部呼叫(BillingGatewayPort)
  • 橫切關注點(LoggerPort、ClockPort)

埠應建模能力,而非技術。

步驟 3:以純編排實作用例

用例類別/函式透過建構子/參數接收埠。它驗證應用層級的不變條件、協調領域規則,並回傳純資料結構。

步驟 4:在邊緣建構適配器

  • 輸入適配器將通訊協定輸入轉換為用例輸入。
  • 輸出適配器將應用合約映射到具體 API/ORM/查詢建構器。
  • 映射保持在適配器內,而非用例內部。

步驟 5:在組合根中注入所有依賴

實例化適配器,然後將它們注入用例。保持此注入集中化,以避免隱藏的服務定位器行為。

步驟 6:按邊界測試

  • 使用虛擬埠對用例進行單元測試。
  • 使用真實基礎設施依賴對適配器進行整合測試。
  • 透過輸入適配器對使用者面向流程進行端到端測試。

架構圖

flowchart LR
  Client["客戶端 (HTTP/CLI/Worker)"] --> InboundAdapter["輸入適配器"]
  InboundAdapter -->|"呼叫"| UseCase["用例 (應用層)"]
  UseCase -->|"使用"| OutboundPort["輸出埠 (介面)"]
  OutboundAdapter["輸出適配器"] -->|"實作"| OutboundPort
  OutboundAdapter --> ExternalSystem["資料庫/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 模型、網頁框架型別或 SDK 客戶端。
  • 用例直接從 req、res 或佇列元資料讀取。
  • 用例直接回傳資料庫行,而沒有經過領域/應用程式的映射。
  • 讓適配器直接互相呼叫,而不是透過用例的埠。
  • 將依賴注入分散到許多檔案中,並使用隱藏的全域單例。

遷移手冊

  1. 選擇一個垂直切片(單一端點/任務),且經常變更帶來痛苦。
  2. 提取用例邊界,包含明確的輸入/輸出型別。
  3. 圍繞現有的基礎設施呼叫引入輸出埠。
  4. 將控制器/服務中的編排邏輯移到用例中。
  5. 保留舊的適配器,但讓它們委派給新的用例。
  6. 在新的邊界周圍添加測試(單元測試 + 適配器整合測試)。
  7. 逐個切片重複;避免全面重寫。

重構現有系統

  • 扼殺者模式:保留當前端點,一次將一個用例路由到新的埠/適配器。
  • 無大爆炸重寫:按功能切片遷移,並透過特徵化測試保留行為。
  • 先建外觀:在替換內部實作之前,先將舊服務包裝在輸出埠之後。
  • 組合凍結:早期集中注入,使新依賴不會洩漏到領域/用例層中。
  • 切片選擇規則:優先選擇變更頻繁且影響範圍小的流程。
  • 回滾路徑:在生產行為得到驗證之前,為每個遷移的切片保留可逆切換或路由開關。

測試指引(相同的六角邊界)

  • 領域測試:將實體/值物件作為純業務規則進行測試(無模擬、無框架設定)。
  • 用例單元測試:使用輸出埠的虛擬/樁件測試編排;斷言業務結果與埠互動。
  • 輸出適配器合約測試:在埠層級定義共享合約套件,並針對每個適配器實作執行。
  • 輸入適配器測試:驗證通訊協定映射(HTTP/CLI/佇列酬載到用例輸入,以及輸出/錯誤回映射到通訊協定)。
  • 適配器整合測試:針對真實基礎設施(資料庫/API/佇列)執行,測試序列化、綱要/查詢行為、重試與逾時。
  • 端到端測試:涵蓋關鍵的使用者旅程,從輸入適配器 -> 用例 -> 輸出適配器。
  • 重構安全性:在提取之前添加特徵化測試;在新建的邊界行為穩定且等效之前,保留這些測試。

最佳實踐檢查清單

  • 領域與用例層僅匯入內部型別與埠。
  • 每個外部依賴都由一個輸出埠表示。
  • 在邊界處進行驗證(輸入適配器 + 用例不變條件)。
  • 使用不可變轉換(回傳新的值/實體,而非修改共享狀態)。
  • 錯誤在邊界之間進行轉換(基礎設施錯誤 -> 應用程式/領域錯誤)。
  • 組合根明確且易於審計。
  • 用例可使用簡單的記憶體虛擬埠進行測試。
  • 重構從一個垂直切片開始,並保留行為的測試。
  • 語言/框架特性僅保留在適配器中,絕不進入領域規則。

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/content-engine

Create platform-native content systems for X, LinkedIn, TikTok, YouTube, newsletters, and repurposed multi-platform campaigns. Use when the user wants social posts, threads, scripts, content calendars, or one source asset adapted cleanly across platforms.

affaan-m/ECC

Multi-platform content distribution across X, LinkedIn, Threads, and Bluesky. Adapts content per platform using content-engine patterns. Never posts identical content cross-platform. Use when the user wants to distribute content across social platforms.

affaan-m/fal-ai-media

Unified media generation via fal.ai MCP — image, video, and audio. Covers text-to-image (Nano Banana), text/image-to-video (Seedance, Kling, Veo 3), text-to-speech (CSM-1B), and video-to-audio (ThinkSound). Use when the user wants to generate images, videos, or audio with AI.

affaan-m/manim-video

日本語翻訳:このファイルは manim-video 用の日本語翻訳が必要です

affaan-m/remotion-video-creation

Remotion のベストプラクティス - React で動画を作成する。3D、アニメーション、音声、字幕、チャート、トランジションなどをカバーするドメイン固有の29のルール。

affaan-m/video-editing

AI-assisted video editing workflows for cutting, structuring, and augmenting real footage. Covers the full pipeline from raw capture through FFmpeg, Remotion, ElevenLabs, fal.ai, and final polish in Descript or CapCut. Use when the user wants to edit video, cut footage, create vlogs, or build video content.

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.

agent-self-evaluation

Use after completing any non-trivial task. The agent self-rates its output on 5 axes — accuracy, completeness, clarity, actionability, conciseness — with concrete evidence per criterion. Produces a structured 1-5 scorecard with specific improvement suggestions.

agent-sort

Build an evidence-backed ECC install plan for a specific repo by sorting skills, commands, rules, hooks, and extras into DAILY vs LIBRARY buckets using parallel repo-aware review passes. Use when ECC should be trimmed to what a project actually needs instead of loading the full bundle.

ai-first-engineering

Engineering operating model for teams where AI agents generate a large share of implementation output. Use when setting team process, review gates, or ownership rules for a codebase largely written by agents.

ai-regression-testing

Regression testing strategies for AI-assisted development. Sandbox-mode API testing without database dependencies, automated bug-check workflows, and patterns to catch AI blind spots where the same model writes and reviews code. Use when adding regression coverage to AI-assisted code, or when the same model both wrote and reviewed a change.

android-clean-architecture

Clean Architecture patterns for Android and Kotlin Multiplatform projects — module structure, dependency rules, UseCases, Repositories, and data layer patterns. Use when structuring modules, layers, or data flow in an Android or KMP project.

angular-developer

Generates Angular code and provides architectural guidance. Trigger when creating projects, components, or services, or for best practices on reactivity (signals, linkedSignal, resource), forms, dependency injection, routing, SSR, accessibility (ARIA), animations, styling (component styles, Tailwind CSS), testing, or CLI tooling.

相關技能