mcpField
H
hexagonal-architecture-layers-java

hexagonal-architecture-layers-java

github.com/gentleman-programming開発661245 回インストール31 週間前に更新
リポジトリ

hexagonal-architecture-layers-java について

Javaサービスの設計やリファクタリングに向けた、ヘキサゴナルアーキテクチャのスキルです。ポートとアダプターを使い、ドメイン、アプリケーション、インフラの分離と依存方向を整理します。

主な機能

  1. 01フレームワークやI/Oに依存しないドメイン
  2. 02アプリケーション層のユースケースとポート
  3. 03ポートを実装するインフラアダプター
  4. 04依存方向とモジュール境界の明確化

活用シーン

  1. 01レイヤー化したJavaサービスの設計
  2. 02ドメインを分離するSpringコードのリファクタリング
  3. 03RESTやメッセージングのアダプター対応
  4. 04永続化処理とドメインモデルの分離

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/gentleman-programming/gentleman-skills --skill hexagonal-architecture-layers-java

インストール後、Claude Code で/hexagonal-architecture-layers-javaを入力して読み込まれたか確認します。

詳細

使用するタイミング

以下の場合に、このスキルを読み込みます。

  • クリーンでテスト可能なレイヤーを備えた新しい Java サービスを設計する
  • ドメインをフレームワークから分離するために Spring コードをリファクタリングする
  • 複数のアダプター(REST + メッセージング、JPA + Mongo)に対応する
  • 依存関係の方向と明確なモジュール境界を徹底する

重要なパターン

パターン 1:ドメインを純粋に保つ

ドメインには、フレームワークのアノテーション、永続化に関する処理、I/O を含めません。

パターン 2:アプリケーションがオーケストレーションする

アプリケーションはユースケースとポートを定義し、ドメインロジックを呼び出して、I/O をポートに委譲します。

パターン 3:インフラストラクチャが適応する

インフラストラクチャはポートを実装し、アダプター(コントローラー、リポジトリー、クライアント)を接続します。

コード例

例 1:ドメインモデル + 出力ポート
package com.acme.order.domain;

public record OrderId(String value) { }

public final class Order {
  private final OrderId id;
  private final Money total;

  public Order(OrderId id, Money total) {
    this.id = id;
    this.total = total;
  }

  public OrderId id() { return id; }
  public Money total() { return total; }
}
package com.acme.order.application.port;

import com.acme.order.domain.Order;
import com.acme.order.domain.OrderId;

public interface OrderRepositoryPort {
  OrderId nextId();
  void save(Order order);
}
例 2:アプリケーションのユースケース + 入力ポート
package com.acme.order.application.usecase;

import com.acme.order.application.port.OrderRepositoryPort;
import com.acme.order.domain.Order;
import com.acme.order.domain.OrderId;
import com.acme.order.domain.Money;

public interface PlaceOrderUseCase {
  OrderId place(Money total);
}

public final class PlaceOrderService implements PlaceOrderUseCase {
  private final OrderRepositoryPort repository;

  public PlaceOrderService(OrderRepositoryPort repository) {
    this.repository = repository;
  }

  @Override
  public OrderId place(Money total) {
    OrderId id = repository.nextId();
    Order order = new Order(id, total);
    repository.save(order);
    return id;
  }
}
例 3:インフラストラクチャのアダプター + 接続
package com.acme.order.infrastructure.persistence;

import com.acme.order.application.port.OrderRepositoryPort;
import com.acme.order.domain.Order;
import com.acme.order.domain.OrderId;
import org.springframework.stereotype.Repository;

@Repository
public final class OrderJpaAdapter implements OrderRepositoryPort {
  private final SpringOrderRepository repository;
  private final OrderMapper mapper;

  public OrderJpaAdapter(SpringOrderRepository repository, OrderMapper mapper) {
    this.repository = repository;
    this.mapper = mapper;
  }

  @Override
  public OrderId nextId() {
    return new OrderId(java.util.UUID.randomUUID().toString());
  }

  @Override
  public void save(Order order) {
    repository.save(mapper.toEntity(order));
  }
}

アンチパターン

禁止:ドメインにフレームワークのアノテーションを配置する
// BAD: domain tied to JPA
@jakarta.persistence.Entity
public class Order {
  @jakarta.persistence.Id
  private String id;
}
禁止:ドメインからインフラストラクチャを直接呼び出す
// BAD: domain depends on Spring repository
public class Order {
  private final SpringOrderRepository repository;
}

クイックリファレンス

タスクパターン
ドメインデータを永続化するアプリケーションに出力ポートを定義し、インフラストラクチャで実装する
ユースケースを公開するアプリケーションに入力ポートとサービスを定義する
ドメインを純粋に保つアノテーション、I/O、フレームワークのインポートを含めない

リソース

コメント