Install any skill in seconds. Free to start, no credit card required.
Get Started Free →ヘキサゴナルアーキテクチャ(ポート・アダプタパターン)、境界の分離、および外部依存関係の管理。
.claude/skills/affaan-m-hexagonal-architecture/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 61% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 124% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 96% | 0% |
| case-17 | ✗→✓ | ▲ Improved | 149% | 0% |
| case-22 | ✗→✓ | ▲ Improved | 68% | 0% |
六边形架构(端口与适配器)使业务逻辑独立于框架、传输层和持久化细节。核心应用依赖于抽象端口,而适配器在边缘实现这些端口。
当需求涉及边界、领域驱动设计、重构紧耦合服务,或将应用逻辑与特定库解耦时,使用此技能。
出站端口接口通常位于应用层(仅当抽象真正属于领域层时才位于领域层),而基础设施适配器实现它们。
依赖方向始终向内:
定义具有清晰输入和输出DTO的单个用例。将传输细节(Express req、GraphQL context、任务负载包装器)保持在此边界之外。
将每个副作用识别为端口:
UserRepositoryPort)BillingGatewayPort)LoggerPort、ClockPort)端口应建模能力,而非技术。
用例类/函数通过构造函数/参数接收端口。它验证应用层不变量,协调领域规则,并返回纯数据结构。
实例化适配器,然后将其注入用例。保持此连接集中化,以避免隐藏的服务定位器行为。
mermaidflowchart LR Client["Client (HTTP/CLI/Worker)"] --> InboundAdapter["Inbound Adapter"] InboundAdapter -->|"calls"| UseCase["UseCase (Application Layer)"] UseCase -->|"uses"| OutboundPort["OutboundPort (Interface)"] OutboundAdapter["Outbound Adapter"] -->|"implements"| OutboundPort OutboundAdapter --> ExternalSystem["DB/API/Queue"] UseCase --> DomainModel["DomainModel"]
使用以功能为先的组织方式,并带有显式边界:
textsrc/ 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
typescriptexport 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 }>; }
typescripttype 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 returns a new Order instance; it does not mutate in place. const authorizedOrder = order.markAuthorized(auth.authorizationId); await this.orderRepository.save(authorizedOrder); return { orderId: order.id, authorizationId: auth.authorizationId, }; } }
typescriptexport 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; } }
typescriptexport const buildCreateOrderUseCase = (deps: { db: SqlClient; stripe: StripeClient }) => { const orderRepository = new PostgresOrderRepository(deps.db); const paymentGateway = new StripePaymentGateway(deps.stripe); return new CreateOrderUseCase(orderRepository, paymentGateway); };
在不同生态系统中使用相同的边界规则;仅语法和连接方式发生变化。
application/ports/* 作为接口/类型。adapters/inbound/*、adapters/outbound/*。domain、application.port.in、application.port.out、application.usecase、adapter.in、adapter.out。application.port.* 中的接口。@Service 是可选的,非必需)。domain、application.port、application.usecase、adapter)。internal/<feature>/domain、application、ports、adapters/inbound、adapters/outbound。New... 构造函数的结构体。cmd/<app>/main.go 中连接(或专用连接包),保持构造函数显式。req、res 或队列元数据读取。| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-16 | pass→pass | 15,663 | 12,509 | -20% | 1 | 1 | 0% | 2,828 | 5,025 | +78% | 0 | 0 | — |
case-01 | fail→pass | 23,641 | 23,331 | -1% | 1 | 1 | 0% | 4,607 | 7,410 | +61% | 0 | 0 | — |
case-02 | pass→pass | 15,063 | 15,808 | +5% | 1 | 1 | 0% | 2,844 | 5,631 | +98% | 0 | 0 | — |
case-03 | pass→pass | 11,991 | 10,344 | -14% | 1 | 1 | 0% | 2,037 | 4,704 | +131% | 0 | 0 | — |
case-04 | pass→pass | 14,507 | 13,822 | -5% | 1 | 1 | 0% | 2,489 | 5,153 | +107% | 0 | 0 | — |
case-05 | pass→pass | 10,536 | 11,938 | +13% | 1 | 1 | 0% | 1,909 | 4,858 | +154% | 0 | 0 | — |
case-06 | pass→pass | 14,318 | 12,687 | -11% | 1 | 1 | 0% | 2,127 | 5,167 | +143% | 0 | 0 | — |
case-07 | fail→pass | 16,551 | 19,730 | +19% | 1 | 1 | 0% | 2,597 | 5,805 | +124% | 0 | 0 | — |
case-08 | pass→pass | 16,669 | 17,563 | +5% | 1 | 1 | 0% | 2,880 | 5,753 | +100% | 0 | 0 | — |
case-09 | pass→fail | 12,474 | 13,379 | +7% | 1 | 1 | 0% | 2,136 | 5,046 | +136% | 0 | 0 | — |
case-10 | fail→pass | 14,261 | 9,375 | -34% | 1 | 1 | 0% | 2,134 | 4,189 | +96% | 0 | 0 | — |
case-11 | pass→pass | 13,574 | 8,959 | -34% | 1 | 1 | 0% | 2,228 | 4,177 | +87% | 0 | 0 | — |
case-12 | pass→pass | 14,283 | 16,044 | +12% | 1 | 1 | 0% | 2,387 | 5,263 | +120% | 0 | 0 | — |
case-13 | pass→pass | 14,382 | 15,414 | +7% | 1 | 1 | 0% | 2,526 | 5,440 | +115% | 0 | 0 | — |
case-14 | pass→pass | 13,161 | 11,958 | -9% | 1 | 1 | 0% | 2,271 | 4,927 | +117% | 0 | 0 | — |
case-15 | pass→pass | 31,817 | 13,664 | -57% | 1 | 1 | 0% | 2,667 | 5,091 | +91% | 0 | 0 | — |
case-17 | fail→pass | 12,342 | 12,903 | +5% | 1 | 1 | 0% | 1,922 | 4,781 | +149% | 0 | 0 | — |
case-18 | pass→pass | 13,468 | 11,975 | -11% | 1 | 1 | 0% | 2,273 | 4,926 | +117% | 0 | 0 | — |
case-19 | pass→pass | 16,590 | 17,091 | +3% | 1 | 1 | 0% | 2,502 | 5,697 | +128% | 0 | 0 | — |
case-20 | pass→pass | 11,798 | 9,912 | -16% | 1 | 1 | 0% | 1,863 | 4,323 | +132% | 0 | 0 | — |
case-21 | fail→fail | 18,863 | 20,320 | +8% | 1 | 1 | 0% | 2,966 | 6,197 | +109% | 0 | 0 | — |
case-22 | fail→pass | 18,991 | 15,637 | -18% | 1 | 1 | 0% | 3,251 | 5,462 | +68% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +18 percentage points is the difference between those two pass rates over the 22 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.