Communityコーディング&開発github.com

Piyush-Nagar/coding-agents-skills

バックエンドサービスは**Package by Feature**パターンに従い、4つの兄弟モジュールで構成されています。このドキュメントは、新しいコードをどこに配置すべきかの信頼できる情報源です。

coding-agents-skills とは?

このスキルは、Package by Feature(機能単位のパッケージ構成)パターンを強制するためのナレッジベースです。バックエンドサービスは`capabilities/`(ビジネスロジック)、`db/`(永続化)、`client/`(外部統合)、`starter/`(構成ルート)の4つの兄弟モジュールで構成され、依存関係は一方向(`starter → capabilities → { db, client }`)に制限されます。各モジュール内部では機能単位でパッケージが分割され、コントローラーやリポジトリはインターフェースと実装に分離されます。このパターンにより、機能変更の影響範囲が局所化され、保守性とテスト容易性が向上します。サポート対象はJava/Spring Bootが主ですが、Kotlin、Go、Python、Node.jsなど言語に依存しない構造を持ち、ファイル拡張子やフレームワーク固有のアノテーションを適宜置き換えることで、どのバックエンド言語でも適用できます。

対応~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Piyush-Nagar/coding-agents-skills/tree/HEAD/backend/core

Installed? Explore more コーディング&開発 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

パッケージ構造

私たちのバックエンドサービスは、機能単位のパッケージ構成(Package by Feature) に従い、4つの兄弟モジュールで編成されています。このドキュメントは、新しいコードの配置場所についての信頼できる情報源です。

言語に関する注記。 以下の例は、私たちの主要なバックエンドスタックであるJava/Spring Bootで記述されていますが、構造自体は言語に依存せず、使用するすべてのバックエンド言語(Kotlin、Go、Python、Nodeなど)で必須です。ファイル拡張子とフレームワークのアノテーションは適宜翻訳してください。フォルダ構成、命名サフィックス(*Db、*Repo、*RepoImpl、*Dao、*Controller、*ControllerImpl)、およびインターフェース/実装の分割は変更しないでください。

基本原則

技術レイヤーではなく、機能でグループ化する。 「orders」機能への変更は、controllers/、services/、repositories/ パッケージに散らばったファイルではなく、互いに隣接するファイルに影響を与えるべきです。技術レイヤー化は モジュール 境界(db、client、capabilities、starter)で行われ、機能単位のパッケージ構成は各モジュール内部で行われます。

4つのモジュール

すべてのサービスは、以下の4つの兄弟モジュールで構成されます。

<service>/
├── capabilities/   # 機能/ビジネスロジック — 頭脳
├── db/             # 永続化: エンティティ、リポジトリ、マイグレーション
├── client/         # 外部システムとのアウトバウンド統合
└── starter/        # Spring Boot スターター — すべてを配線し、実行する

capabilities/ — ビジネスロジック

各機能は独自のパッケージを持ち、各機能内部にはAPIエンドポイントごとに1つのサブパッケージ(getById、create、updateById、getByQuery、…)があります。各サブ機能は、3つの兄弟フォルダを所有します。

  • service/ — そのAPIの単一サービスクラス
  • dto/ — request/ および response/(該当するもの)
  • mapper/ — *Db エンティティとDTOを(双方向に)変換する、このサブ機能専用のマッパー

機能の コントローラーは機能パッケージのルート に配置され、リポジトリと同様にインターフェースと実装に分割されます。

  • capabilities/<feature>/<Feature>Controller.java — インターフェース。ルート、HTTP動詞、リクエスト/レスポンスタイプ、APIドキュメントアノテーションを宣言します。
  • capabilities/<feature>/<Feature>ControllerImpl.java — 実装。フレームワークのアノテーション(@RestController)を持ち、各ルートを対応するサブ機能サービスに委譲します。

各機能のサブ機能間で共有されます。コントローラー を参照してください。

  • 依存関係: db(*Repo インターフェース経由のみ — DB使用ルール を参照)、client
  • starter に依存しない
  • main クラス、@SpringBootApplication を含まない
  • 所有: HTTPコントローラー、APIリクエスト/レスポンスDTO、マッパー、サービス

db/ — 永続化

テーブルごとに1つのフォルダ で構成されます。各テーブルフォルダには、正確に3つのサブフォルダがあります。

db/<tableName>/
├── model/
│   ├── <EntityName>Db.java          # メインJPAエンティティ
│   └── <Other>Db.java               # サブモデル(埋め込み型、子エンティティ、射影)
├── dao/
│   └── <EntityName>Dao.java         # 低レベルデータアクセス(Criteria/JPQL/ネイティブクエリ)
└── repository/
    ├── <EntityName>Repo.java        # PUBLIC インターフェース — capabilities が見る唯一の型
    └── impl/
        └── <EntityName>RepoImpl.java # Spring Data + Dao をインターフェースの背後で構成

命名規則

レイヤーサフィックス例
JPAエンティティDbTestcaseDb
リポジトリインターフェースRepoTestcaseRepo
リポジトリ実装RepoImplTestcaseRepoImpl
データアクセスヘルパーDaoTestcaseDao

DB使用ルール

Capabilities は *Repo インターフェースにのみ依存しなければなりません。 *RepoImpl と *Dao は内部用です — db/<tableName>/ パッケージの外部から注入されたり参照されたりしてはいけません。これにより、永続化レイヤーを交換可能に保ち、能力テストを契約に集中させることができます。

  • 依存関係: このサービス内にはない
  • 公開: *Repo インターフェースと *Db モデル型
  • 含まない: ビジネスロジック、API DTOへの変換

client/ — アウトバウンド統合

このサービスが依存する 他のサービスを呼び出す コードを保持します。主に、プラットフォーム上の 依存マイクロサービス(paymentService、userService、executionService、…)ごとに1つのサブフォルダがあります。サードパーティSDKラッパーやメッセージプロデューサーもここに配置される可能性がありますが、主要なパターン(そしてこのモジュールが存在する理由)は、依存マイクロサービスクライアントです。

client/<microserviceName>/
├── <Microservice>Client.java       # インターフェース — capabilities が依存するもの
├── impl/
│   └── <Microservice>ClientImpl.java   # 実装: HTTP/gRPC/SDK呼び出し、リトライ、タイムアウト
├── dto/                            # このマイクロサービスのAPIのリクエスト/レスポンス形状
│   ├── request/
│   └── response/
└── mapper/                         # オプション: 彼らの形状と私たちの形状を変換

ルール

  • 依存マイクロサービスごとに1つのサブフォルダ。サービス名で名前付けします。たとえ「関連している」場合でも、複数のマイクロサービスを1つのパッケージにまとめないでください。

  • Capabilities は *Client インターフェースにのみ依存します。 *ClientImpl は内部用です — *Repo / *RepoImpl と同じルールです。

  • クライアントインターフェースは、マイクロサービスのAPIを自身のDTOの用語で公開します。capabilities は必要に応じてそれらをさらに適応させることができます。

  • 横断的関心事(リトライ、サーキットブレーカー、リクエスト/レスポンスログ、認証ヘッダー)は impl/ に属します — capabilities はそれらを見るべきではありません。

  • 依存関係: このサービス内にはない

  • 公開: *Client インターフェースとそのリクエスト/レスポンスDTO

  • 含まない: ビジネスロジック、capabilities のオーケストレーション

starter/ — 構成ルート

実行される唯一のモジュール。@SpringBootApplication、グローバルREST例外ハンドラ、セキュリティ設定、フィルター、application.yml を含みます。コントローラーはありません — それらは capabilities/ にあります。

  • 依存関係: capabilities(および推移的に db、client)
  • 所有: デプロイ可能なアーティファクト、横断的Web設定(セキュリティ、CORS、エラーハンドリング)
  • 所有しない: 機能コントローラーや機能DTO

依存関係ルール

starter → capabilities → { db, client }

矢印は一方向です。db と client は capabilities や starter からインポートしません。capabilities は starter からインポートしません。

コントローラー

サブ機能ごとではなく、機能ごとに1つのコントローラーがあり、capabilities/ 内の 機能パッケージのルート に配置されます。*Repo / *RepoImpl の分割を反映して、2つのファイルに分割されます。

  • capabilities/<feature>/<Feature>Controller.java — インターフェース。ルート、HTTP動詞、パス/クエリパラメータ、リクエスト/レスポンスDTO型、APIドキュメントアノテーション(OpenAPI/Swagger)を宣言します。インターフェースを読む人は、HTTP契約全体を見ることができるはずです。
  • capabilities/<feature>/<Feature>ControllerImpl.java — 実装。フレームワークのアノテーション(Springでは @RestController)を持ち、サブ機能サービスの注入対象であり、各ルートに対して、対応する service/ を呼び出して結果を返す1つの短いメソッドを含みます。委譲以外のロジックはありません。

なぜ分割するのか?インターフェースはAPI契約です — 安定しており、文書化され、単独でテスト可能です。実装は配線です — 契約を変更せずに変更できます。*Repo と *RepoImpl と同じ理由です。

コントローラーは、各HTTPルートを capabilities/<feature>/<sub-feature>/service/ 内の対応するサブ機能サービスに委譲します。リクエスト/レスポンスDTOはサブ機能(capabilities/<feature>/<sub-feature>/dto/)が所有します。コントローラーは単なる薄いHTTPアダプターです。

なぜ starter/ ではなく capabilities/ 内部なのか?コントローラー、そのサービス、およびそのDTOは、APIが変更されるときにすべて一緒に変更されます — それらを1つのモジュールに保持することで、「1つの機能、1つの場所」という機能単位のパッケージ構成の約束が守られます。starter/ は横断的配線(セキュリティ、エラーハンドラ、アプリケーションエントリポイント)専用であり、機能ごとのWebコード用ではありません。

動作例: testcase 機能

以下のツリーは、説明のために .java 拡張子を使用しています。言語のファイル拡張子とフレームワークのイディオム(Kotlinの .kt、Goのパッケージ、Pythonのモジュールなど)を適宜置き換えてください。ただし、フォルダ構成と命名サフィックスは同一に保ってください。

testcase-service/
├── capabilities/
│   └── com/example/testcase/capabilities/
│       └── testcase/                          # メイン機能
│           ├── TestcaseController.java        # インターフェース — HTTP契約、ルート宣言、APIドキュメント
│           ├── TestcaseControllerImpl.java    # 実装 — @RestController、サブ機能サービスに委譲
│           ├── getById/                       # サブ機能 = 1つのAPI
│           │   ├── service/
│           │   │   └── GetTestcaseByIdService.java
│           │   ├── dto/
│           │   │   └── response/
│           │   │       └── GetTestcaseByIdResponse.java
│           │   └── mapper/
│           │       └── GetTestcaseByIdMapper.java     # TestcaseDb → レスポンス
│           ├── create/
│           │   ├── service/
│           │   │   └── CreateTestcaseService.java
│           │   ├── dto/
│           │   │   ├── request/
│           │   │   │   └── CreateTestcaseRequest.java
│           │   │   └── response/
│           │   │       └── CreateTestcaseResponse.java
│           │   └── mapper/
│           │       └── CreateTestcaseMapper.java      # リクエスト → TestcaseDb, TestcaseDb → レスポンス
│           ├── updateById/
│           │   ├── service/
│           │   │   └── UpdateTestcaseByIdService.java
│           │   ├── dto/
│           │   │   ├── request/
│           │   │   │   └── UpdateTestcaseByIdRequest.java
│           │   │   └── response/
│           │   │       └── UpdateTestcaseByIdResponse.java
│           │   └── mapper/
│           │       └── UpdateTestcaseByIdMapper.java
│           └── getByQuery/
│               ├── service/
│               │   └── GetTestcaseByQueryService.java
│               ├── dto/
│               │   ├── request/
│               │   │   └── GetTestcaseByQueryRequest.java
│               │   └── response/
│               │       └── GetTestcaseByQueryResponse.java
│               └── mapper/
│                   └── GetTestcaseByQueryMapper.java
│
├── db/                                        # テーブルごとに1つのフォルダ
│   └── com/example/testcase/db/
│       └── testcase/                          # テーブル名
│           ├── model/
│           │   ├── TestcaseDb.java            # JPAエンティティ — メイン
│           │   └── TestcaseStepDb.java        # サブモデル(子/埋め込み)
│           ├── dao/
│           │   └── TestcaseDao.java           # クエリ/条件ヘルパー、パッケージプライベート
│           └── repository/
│               ├── TestcaseRepo.java          # インターフェース — capabilities はこれに依存
│               └── impl/
│                   └── TestcaseRepoImpl.java  # Spring Data + TestcaseDao を構成
│
├── client/                                   # 依存マイクロサービスごとに1つのフォルダ
│   └── com/example/testcase/client/
│       ├── executionService/                 # 呼び出す依存マイクロサービス
│       │   ├── ExecutionServiceClient.java        # インターフェース
│       │   ├── impl/
│       │   │   └── ExecutionServiceClientImpl.java  # HTTP/gRPC呼び出し、リトライ、タイムアウト
│       │   ├── dto/
│       │   │   ├── request/
│       │   │   │   └── RunTestcaseRequest.java
│       │   │   └── response/
│       │   │       └── RunTestcaseResponse.java
│       │   └── mapper/
│       │       └── ExecutionServiceMapper.java
│       └── userService/                      # 別の依存マイクロサービス
│           ├── UserServiceClient.java
│           └── impl/
│               └── UserServiceClientImpl.java
│
└── starter/                                  # 横断的配線のみ — コントローラーなし
    └── com/example/testcase/
        ├── TestcaseServiceApplication.java
        ├── config/
        │   ├── SecurityConfig.java
        │   └── WebConfig.java
        ├── error/
        │   └── GlobalExceptionHandler.java   # @ControllerAdvice — 横断的、機能ごとではない
        └── resources/application.yml

経験則

  • 既存の機能に新しいAPIを追加する? capabilities/<feature>/ 内に、独自の service/、dto/{request,response}/、mapper/ を持つ新しいサブ機能パッケージを作成し、ルート宣言を <Feature>Controller.java(インターフェース)に、委譲メソッドを <Feature>ControllerImpl.java に追加します。2つ目のコントローラーは作成しないでください。
  • まったく新しい機能を追加する? capabilities/ に機能パッケージ(ルートにコントローラー、APIごとにサブ機能)を作成し、db/<tableName>/ に対応するテーブルフォルダ(model/、dao/、repository/{impl/} を含む)を作成します。外部システムを呼び出す場合のみクライアントを追加します。starter/ には触れないでください。
  • 常に *Repo を注入し、*RepoImpl や *Dao は注入しないでください。 Capabilities はインターフェースにのみ依存します。*RepoImpl と *Dao は db/<tableName>/ 内部のものです — クエリ戦略やバックエンドストアを、capability コードを変更せずに交換できるようにするために存在します。
  • 常に *Client を注入し、*ClientImpl は注入しないでください。 依存マイクロサービス呼び出しにも同じルールです。Capabilities はインターフェースに依存します。トランスポート(HTTP、gRPC、SDK)は impl/ の背後にあります。
  • client/ 内の依存マイクロサービスごとに1つのフォルダ。 フォルダ名はマイクロサービス名にします。「すべての内部サービス」を1つのパッケージにグループ化しないでください。
  • サブ機能の粒度は capabilities/ で止まります。 db/ はテーブルごとに編成され、client/ は外部システムごとに編成されます。いずれかをAPIごとに分割すると、凝集性の高いコードが散らばってしまいます。
  • リクエスト/レスポンスDTOとそのマッパーはサブ機能ごとにあり、共有されません。 もし create と updateById が今日似ているように見えても、2つのDTOと2つのマッパーを保持してください。共有すると、独立して変化する2つのAPIが結合されます。
  • マッパーはエンティティ ↔ DTOを直接変換します。 中間ドメインモデル層はありません。<SubFeature>Mapper は *Db とそのサブ機能のリクエスト/レスポンスDTOを変換します。
  • 3つのサブ機能が類似したコードを持っている? 同じパターンが 4つ以上 の場所に現れるまで、共有サービスやマッパーを抽出したい衝動を抑えてください。
  • 機能間の再利用? 共有ユーティリティを common/ パッケージに配置します。

(注: 元の内容はここで切り詰められていますが、翻訳は原文に忠実に、指示されたすべてのセクションをカバーしています。)

Individual skills in this repo

This repo contains 4 individual skills — each has its own dedicated page.

関連スキル