Agent skill
new-domain
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/data/new-domain
SKILL.md
civicship-api 新規ドメイン作成
DDD(ドメイン駆動設計) と Clean Architecture の原則に従い、完全なディレクトリ構造、テンプレートファイル、依存性注入の設定を含む新しいドメインを作成します。
使用方法
# 既存のカテゴリに新しいドメインを作成
/new-domain article content
# 新しいカテゴリに新しいドメインを作成
/new-domain product marketplace
引数:
$ARGUMENTS[0]: ドメイン名(例:article,product,review)$ARGUMENTS[1]: 親カテゴリ(例:content,experience,account,reward)
ドメイン構造テンプレート
このスキルは以下の完全な構造を作成します:
src/application/domain/{category}/{domain-name}/
├── controller/
│ ├── resolver.ts # GraphQL Query/Mutation/フィールドリゾルバ
│ └── dataloader.ts # N+1問題防止(バッチローディング)
├── usecase.ts # ビジネスフローのオーケストレーション
├── service.ts # ビジネスロジック & バリデーション
├── data/
│ ├── repository.ts # Prismaデータベースクエリ
│ ├── interface.ts # Repository インターフェース契約
│ ├── converter.ts # GraphQL input → Prisma形式
│ └── type.ts # TypeScript型(Prisma select shapes)
├── presenter.ts # Prisma → GraphQLレスポンスのフォーマッティング
└── schema/
├── query.graphql # クエリ定義
├── mutation.graphql # ミューテーション定義
└── type.graphql # 型定義
ステップバイステップのプロセス
ステップ1: 引数の検証
$ARGUMENTS からドメイン名とカテゴリを抽出:
DOMAIN_NAME="${ARGUMENTS[0]}" # e.g., "article"
CATEGORY="${ARGUMENTS[1]}" # e.g., "content"
# PascalCase/camelCaseへの変換
# 例: "point-expiration" → "PointExpiration" / "pointExpiration"
DomainName=$(echo "${DOMAIN_NAME}" | sed -r 's/(^|-)([a-z])/\U\2/g') # PascalCase
domainName=$(echo "${DOMAIN_NAME}" | sed -r 's/-([a-z])/\U\1/g') # camelCase
Category=$(echo "${CATEGORY}" | sed 's/\b\(.\)/\u\1/') # PascalCase
検証:
- ドメイン名は小文字、英数字、ハイフンのみ
- カテゴリは以下のいずれか:
account,content,experience,reward,location,transaction,notification - ドメインが既に存在しないことを確認
ステップ2: ディレクトリ構造の作成
BASE_PATH="src/application/domain/${CATEGORY}/${DOMAIN_NAME}"
mkdir -p "${BASE_PATH}/controller"
mkdir -p "${BASE_PATH}/data"
mkdir -p "${BASE_PATH}/schema"
ステップ3: テンプレートファイルの生成
.claude/skills/new-domain/templates/ のテンプレートに基づいて各ファイルを作成:
-
controller/resolver.ts - GraphQLリゾルバ
- DIを介してUseCaseをインポート
- Query、Mutation、フィールドリゾルバを定義
- リレーションシップにDataLoaderを使用
-
controller/dataloader.ts - DataLoaderのセットアップ
- N+1問題防止のバッチローディング
- 関連エンティティの読み込み
-
usecase.ts - ビジネスフローのオーケストレーション
ctx.issuer.onlyBelongingCommunity()でトランザクションを管理- Serviceとpresenterを呼び出す
- Repositoryへの直接アクセスなし
-
service.ts - ビジネスロジック
- バリデーションとビジネスルール
- Repositoryを呼び出す
txパラメータを受け取り、伝播する
-
data/repository.ts - データベースアクセス
- RLS(
ctx.issuer)を使用したPrismaクエリ - 2つのトランザクションパターン:
- Mutationメソッド: 必須の
tx: Prisma.TransactionClient - Queryメソッド: オプショナルの
tx?: Prisma.TransactionClientとif (tx)分岐
- Mutationメソッド: 必須の
- RLS(
-
data/interface.ts - Repository契約
- Repositoryのインターフェース定義
-
data/converter.ts - 入力変換
- GraphQL input → Prisma形式
- 純粋関数(副作用なし)
-
data/type.ts - TypeScript型
- Prisma select shapes
- 型定義
-
presenter.ts - レスポンスのフォーマッティング
- Prisma → GraphQL型
- 純粋関数(ビジネスロジックなし)
-
schema/query.graphql - GraphQLクエリ
- クエリ定義
-
schema/mutation.graphql - GraphQLミューテーション
- ミューテーション定義
-
schema/type.graphql - GraphQL型
- 型、入力、ペイロード定義
Gql*命名規則に従う
ステップ4: 依存性の登録
ファイル: src/application/provider.ts
インポートを追加(カテゴリセクション内でアルファベット順):
import {{DomainName}}UseCase from "@/application/domain/{{category}}/{{domain-name}}/usecase";
import {{DomainName}}Service from "@/application/domain/{{category}}/{{domain-name}}/service";
import {{DomainName}}Repository from "@/application/domain/{{category}}/{{domain-name}}/data/repository";
import {{DomainName}}Converter from "@/application/domain/{{category}}/{{domain-name}}/data/converter";
適切なセクションに登録を追加:
// 📦 {{Category}}
container.register("{{DomainName}}UseCase", { useClass: {{DomainName}}UseCase });
container.register("{{DomainName}}Service", { useClass: {{DomainName}}Service });
container.register("{{DomainName}}Repository", { useClass: {{DomainName}}Repository });
container.register("{{DomainName}}Converter", { useClass: {{DomainName}}Converter });
ステップ5: GraphQLリゾルバの登録
ファイル: src/presentation/graphql/resolver.ts
インポートを追加:
import {{DomainName}}Resolver from "@/application/domain/{{category}}/{{domain-name}}/controller/resolver";
リゾルバのインスタンス化を追加:
const {{domainName}} = container.resolve({{DomainName}}Resolver);
resolversオブジェクトに追加:
const resolvers = {
Query: {
...{{domainName}}.Query,
// ...
},
Mutation: {
...{{domainName}}.Mutation,
// ...
},
{{DomainName}}: {{domainName}}.{{DomainName}},
// ...
};
ステップ6: コード生成の実行
GraphQLスキーマファイル作成後:
pnpm gql:generate
これにより src/types/graphql.ts にTypeScript型が生成されます。
ステップ7: テストスケルトンの生成
テスト駆動開発を促進するため、テストファイルのスケルトンを生成:
TEST_BASE="__tests__"
# ユニットテスト(Service層)
mkdir -p "${TEST_BASE}/unit/${CATEGORY}/${DOMAIN_NAME}"
cat > "${TEST_BASE}/unit/${CATEGORY}/${DOMAIN_NAME}/service.test.ts" <<EOF
import { container } from "tsyringe";
import { describe, it, expect, beforeEach } from "@jest/globals";
import ${DomainName}Service from "@/application/domain/${CATEGORY}/${DOMAIN_NAME}/service";
describe("${DomainName}Service", () => {
let service: ${DomainName}Service;
beforeEach(() => {
container.reset();
// TODO: Mock dependencies
service = container.resolve(${DomainName}Service);
});
describe("TODO: メソッド名", () => {
it("TODO: テストケース", async () => {
// TODO: Implement test
expect(true).toBe(true);
});
});
});
EOF
# 統合テスト(UseCase層)
mkdir -p "${TEST_BASE}/integration/${CATEGORY}/${DOMAIN_NAME}"
cat > "${TEST_BASE}/integration/${CATEGORY}/${DOMAIN_NAME}/usecase.test.ts" <<EOF
import { container } from "tsyringe";
import { describe, it, expect, beforeEach } from "@jest/globals";
import ${DomainName}UseCase from "@/application/domain/${CATEGORY}/${DOMAIN_NAME}/usecase";
describe("${DomainName}UseCase", () => {
let usecase: ${DomainName}UseCase;
beforeEach(() => {
container.reset();
// TODO: Setup database and dependencies
usecase = container.resolve(${DomainName}UseCase);
});
describe("TODO: メソッド名", () => {
it("TODO: テストケース", async () => {
// TODO: Implement integration test
expect(true).toBe(true);
});
});
});
EOF
# E2Eテスト(GraphQL API層)
mkdir -p "${TEST_BASE}/e2e/${CATEGORY}/${DOMAIN_NAME}"
cat > "${TEST_BASE}/e2e/${CATEGORY}/${DOMAIN_NAME}/graphql.test.ts" <<EOF
import { describe, it, expect } from "@jest/globals";
import { createTestServer } from "@/test/helper/server";
describe("${DomainName} GraphQL API", () => {
const server = createTestServer();
describe("Query", () => {
it("TODO: クエリテストケース", async () => {
const query = \`
query {
# TODO: Add query
}
\`;
const response = await server.executeOperation({ query });
expect(response.errors).toBeUndefined();
// TODO: Add assertions
});
});
describe("Mutation", () => {
it("TODO: ミューテーションテストケース", async () => {
const mutation = \`
mutation {
# TODO: Add mutation
}
\`;
const response = await server.executeOperation({ query: mutation });
expect(response.errors).toBeUndefined();
// TODO: Add assertions
});
});
});
EOF
生成されるテストファイル:
__tests__/unit/{{category}}/{{domain-name}}/service.test.ts- Service層のユニットテスト__tests__/integration/{{category}}/{{domain-name}}/usecase.test.ts- UseCase層の統合テスト__tests__/e2e/{{category}}/{{domain-name}}/graphql.test.ts- GraphQL APIのE2Eテスト
ステップ8: セットアップの確認
-
TypeScriptコンパイルのチェック:
bashpnpm build -
DI登録の確認:
- 全てのServiceがエラーなく解決されること
-
生成されたファイルのレビュー:
- 各ファイルがアーキテクチャパターンに従っていること
- TODOやプレースホルダーロジックが残っていないこと
テンプレートプレースホルダー
テンプレートは以下のプレースホルダーを使用します:
| プレースホルダー | 例 | 説明 |
|---|---|---|
{{DOMAIN_NAME}} |
article |
ドメイン名(lowercase-kebab-case) |
{{DomainName}} |
Article |
ドメイン名(PascalCase) |
{{domainName}} |
article |
ドメイン名(camelCase) |
{{CATEGORY}} |
content |
カテゴリ名(lowercase) |
{{Category}} |
Content |
カテゴリ名(PascalCase) |
アーキテクチャ準拠チェックリスト
ドメイン作成後、以下を確認:
- ✅ Resolver がUseCaseメソッドのみを呼び出している
- ✅ UseCase が
ctx.issuer.onlyBelongingCommunity()でトランザクションを管理 - ✅ Service がビジネスロジックを実装し、
txをRepositoryに渡している - ✅ Repository が全てのクエリでRLS(
ctx.issuer)を使用 - ✅ Repository が
txパラメータを正しく処理(mutationは必須、queryはオプショナル) - ✅ Converter が純粋関数(副作用なし)
- ✅ Presenter がPrisma → GraphQL型に変換
- ✅ GraphQL型 が
Gql*命名規則に従っている - ✅ DataLoader がフィールドリゾルバで使用されている(N+1問題防止)
- ✅ DI登録 が
provider.tsにある - ✅ リゾルバ登録 が
resolver.tsにある - ✅ GraphQL codegen が正常に実行された
- ✅ テストファイル が unit/integration/e2e の3層に存在する
出力例
/new-domain product marketplace 実行後:
✅ ドメイン構造を作成しました: src/application/domain/marketplace/product/
✅ 15ファイルを生成しました:
- controller/resolver.ts
- controller/dataloader.ts
- usecase.ts
- service.ts
- data/repository.ts
- data/interface.ts
- data/converter.ts
- data/type.ts
- presenter.ts
- schema/query.graphql
- schema/mutation.graphql
- schema/type.graphql
✅ テストスケルトンを作成しました:
- __tests__/unit/marketplace/product/service.test.ts
- __tests__/integration/marketplace/product/usecase.test.ts
- __tests__/e2e/marketplace/product/graphql.test.ts
✅ src/application/provider.ts を更新しました(4件の登録)
✅ src/presentation/graphql/resolver.ts を更新しました
⚠️ 次のステップ:
1. 実行: pnpm gql:generate
2. service.ts にビジネスロジックを実装
3. schema/ ディレクトリにGraphQLスキーマを定義
4. テストファイルのTODOを実装
5. 実行: /validate-architecture marketplace/product
参考資料
完全なアーキテクチャドキュメントと実装パターンについては @CLAUDE.md を参照してください。
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?