Communitygithub.com

六边形架构

设计、实现和重构端口与适配器系统,确保清晰的领域边界、依赖反转以及可测试的用例编排,支持 TypeScript、Java、Kotlin 和 Go 服务。在引入或重构为端口与适配器架构,或领域逻辑与 I/O 纠缠不清时使用。

六边形架构 是什么?

该技能提供了六边形架构(端口与适配器)的完整实施指南,帮助开发者构建业务逻辑独立于框架、传输层和持久化细节的系统。核心原则是依赖方向始终向内,领域层不依赖任何外部框架或基础设施,应用层依赖抽象的端口接口,适配器在边缘实现这些端口。技能详细讲解了从建模用例边界、定义出站端口、实现用例编排、构建适配器到在组合根中完成依赖注入的完整步骤,并提供了 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 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

六边形架构 是做什么的?

六边形架构(端口与适配器)使业务逻辑独立于框架、传输层和持久化细节。核心应用依赖于抽象的端口,适配器在边缘实现这些端口。

何时使用

  • 构建需要长期可维护性和可测试性的新功能。
  • 重构分层或框架密集型代码,其中领域逻辑与 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 模型、Web 框架类型或 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/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.

相关技能