mcpField
S
spring-boot-engineer

spring-boot-engineer

github.com/farmage開発224283 回インストール5 週間前に更新
リポジトリ

spring-boot-engineer について

アーキテクチャやレイヤー構成からセキュリティ、テスト、デプロイ確認まで扱う Spring Boot 開発スキルです。コンストラクターインジェクション、API 入力検証、Actuator によるヘルスチェックを重視します。

主な機能

  1. 01レイヤー化されたサービス設計の指針
  2. 02REST API と入力検証
  3. 03Spring Security と OAuth2 の指針
  4. 04ユニット・統合・スライステスト
  5. 05Actuator のヘルスチェックと可観測性

活用シーン

  1. 01Spring Boot の REST 機能のひな型作成
  2. 02エンドポイントへの検証とセキュリティ追加
  3. 03サービス層と Web 層のテスト
  4. 04デプロイ前のアプリケーション状態確認

インストール

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

$npx skills add https://github.com/farmage/opencode-skills --skill spring-boot-engineer

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

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
cloud.md
data.md
security.md
testing.md
web.md

詳細

Spring Boot エンジニア

コアワークフロー

  1. 要件を分析する — サービスの境界、API、データモデル、セキュリティ要件を特定します
  2. アーキテクチャを設計する — マイクロサービス、データアクセス、クラウド連携、セキュリティを計画し、コーディング前に設計を確認します
  3. 実装する — コンストラクターインジェクションとレイヤードアーキテクチャを使用してサービスを作成します(以下のクイックスタートを参照)
  4. セキュリティを確保する — Spring Security、OAuth2、メソッドセキュリティ、CORS 設定を追加し、セキュリティルールがコンパイルされ、テストに合格することを確認します。コンパイルまたはテストに失敗した場合は、エラー出力を確認し、失敗したルールまたは設定を修正してから、次に進む前に再実行します
  5. テストする — 単体テスト、統合テスト、スライステストを作成し、./mvnw test(または ./gradlew test)を実行して、次に進む前にすべて合格することを確認します。テストに失敗した場合は、スタックトレースを確認し、失敗したアサーションまたはコンポーネントを特定して問題を修正し、テストスイート全体を再実行します
  6. デプロイする — Actuator を介してヘルスチェックと可観測性を設定し、/actuator/health が UP を返すことを確認します。ヘルス状態が DOWN の場合は、レスポンスの components の詳細を確認し、失敗したコンポーネント(例:データソース、ブローカー)を解決してから、再検証します

リファレンスガイド

状況に応じて詳細なガイダンスを読み込みます。

トピックリファレンス読み込むタイミング
Web レイヤーreferences/web.mdコントローラー、REST API、バリデーション、例外処理
データアクセスreferences/data.mdSpring Data JPA、リポジトリー、トランザクション、プロジェクション
セキュリティreferences/security.mdSpring Security 6、OAuth2、JWT、メソッドセキュリティ
クラウドネイティブreferences/cloud.mdSpring Cloud、Config、Discovery、Gateway、レジリエンス
テストreferences/testing.md@SpringBootTest、MockMvc、Testcontainers、テストスライス

クイックスタート — 最小限の動作する構成

標準的な Spring Boot の機能は、以下のレイヤーで構成されます。これらをコピーして貼り付けるための出発点として使用してください。

エンティティ
@Entity
@Table(name = "products")
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotBlank
    private String name;

    @DecimalMin("0.0")
    private BigDecimal price;

    // getters / setters or use @Data (Lombok)
}
リポジトリー
public interface ProductRepository extends JpaRepository<Product, Long> {
    List<Product> findByNameContainingIgnoreCase(String name);
}
サービス(コンストラクターインジェクション)
@Service
public class ProductService {
    private final ProductRepository repo;

    public ProductService(ProductRepository repo) { // constructor injection — no @Autowired
        this.repo = repo;
    }

    @Transactional(readOnly = true)
    public List<Product> search(String name) {
        return repo.findByNameContainingIgnoreCase(name);
    }

    @Transactional
    public Product create(ProductRequest request) {
        var product = new Product();
        product.setName(request.name());
        product.setPrice(request.price());
        return repo.save(product);
    }
}
REST コントローラー
@RestController
@RequestMapping("/api/v1/products")
@Validated
public class ProductController {
    private final ProductService service;

    public ProductController(ProductService service) {
        this.service = service;
    }

    @GetMapping
    public List<Product> search(@RequestParam(defaultValue = "") String name) {
        return service.search(name);
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public Product create(@Valid @RequestBody ProductRequest request) {
        return service.create(request);
    }
}
DTO(record)
public record ProductRequest(
    @NotBlank String name,
    @DecimalMin("0.0") BigDecimal price
) {}
グローバル例外ハンドラー
@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Map<String, String> handleValidation(MethodArgumentNotValidException ex) {
        return ex.getBindingResult().getFieldErrors().stream()
            .collect(Collectors.toMap(FieldError::getField, FieldError::getDefaultMessage));
    }

    @ExceptionHandler(EntityNotFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public Map<String, String> handleNotFound(EntityNotFoundException ex) {
        return Map.of("error", ex.getMessage());
    }
}
テストスライス
@WebMvcTest(ProductController.class)
class ProductControllerTest {
    @Autowired MockMvc mockMvc;
    @MockBean ProductService service;

    @Test
    void createProduct_validRequest_returns201() throws Exception {
        var product = new Product(); product.setName("Widget"); product.setPrice(BigDecimal.TEN);
        when(service.create(any())).thenReturn(product);

        mockMvc.perform(post("/api/v1/products")
                .contentType(MediaType.APPLICATION_JSON)
                .content("""{"name":"Widget","price":10.0}"""))
            .andExpect(status().isCreated())
            .andExpect(jsonPath("$.name").value("Widget"));
    }
}

制約事項

必ず行うこと
ルール正しいパターン
コンストラクターインジェクションpublic MyService(Dep dep) { this.dep = dep; }
API 入力を検証するすべての変更系エンドポイントで @Valid @RequestBody MyRequest req を使用する
型安全な設定レコードまたはクラスにバインドされた @ConfigurationProperties(prefix = "app")
適切なステレオタイプビジネスロジックには @Service、データには @Repository、HTTP には @RestController
トランザクションの範囲複数ステップの書き込みには @Transactional、読み取りには @Transactional(readOnly = true)
内部情報を隠す@RestControllerAdvice でドメイン例外をキャッチし、スタックトレースではなく問題の詳細を返す
シークレットを外部化する環境変数または Spring Cloud Config を使用し、application.properties は決して使用しない
絶対に行わないこと
  • フィールドインジェクションを使用する(フィールドに @Autowired を付ける)
  • API エンドポイントで入力検証を省略する
  • @Service/@Repository/@Controller が適切な場合に @Component を使用する
  • ブロッキングコードとリアクティブコードを混在させる(例:WebFlux チェーン内で .block() を呼び出す)
  • application.properties/application.yml にシークレットや認証情報を保存する
  • URL、認証情報、環境固有の値をハードコードする
  • 非推奨の Spring Boot 2.x パターンを使用する(例:WebSecurityConfigurerAdapter)

ドキュメント

コメント