Backend Coding Guidelines - What AI Gets Wrong
This skill focuses on patterns AI commonly fails to implement correctly in Laravel applications following a 7-layer Laravel-native architecture.
Table of Contents
- •How to Use This Skill
- •Architecture Overview
- •AI's Critical Weaknesses - Quick Reference
- •Naming Conventions
- •Method Naming
- •Class Modifiers
- •Prohibited Patterns
- •AI Weakness Checklist
- •Summary: What to Watch For
How to Use This Skill
Quick Reference - Phase 2: Implementation & Review
実装前チェック:
- • 実装対象に応じたパターンを確認(UseCase/Repository/Service/DTO)
- • AI's Critical Weaknessesセクションで注意点を把握
- • Prohibited Patternsを確認
実装後チェック:
- • AI Weakness Checklistで自己チェック
- • 禁止パターンが使用されていないか確認
- • レイヤー分離ルールが守られているか確認
詳細な規約:
- •
.claude/rules/backend/- レイヤー構造、DTO、テスト、コーディング規約の詳細
Architecture Overview
7-Layer Structure (Laravel-native):
┌─────────────────────────────────────────┐ │ Presentation (Controllers) │ ← HTTP Request/Response ├─────────────────────────────────────────┤ │ Request (FormRequests) │ ← Validation & DTO Conversion ├─────────────────────────────────────────┤ │ UseCase (Business Logic) │ ← Application Logic ├─────────────────────────────────────────┤ │ Service (Shared Logic) │ ← Reusable Business Logic ├─────────────────────────────────────────┤ │ Repository (Data Access) │ ← Data Abstraction ├─────────────────────────────────────────┤ │ Model (Eloquent) │ ← Domain Models ├─────────────────────────────────────────┤ │ Resource (JSON Transformation) │ ← Response Transformation └─────────────────────────────────────────┘
Directory Structure (app/ 配下にフラット配置):
app/ ├── Http/ │ ├── Controllers/ │ │ ├── Api/ # API Controllers │ │ └── Web/ # Web Controllers(Inertia.js) │ ├── Requests/ # FormRequests │ └── Resources/ # API Resources ├── UseCases/ # UseCases ├── Services/ # Services ├── Repositories/ # Repositories ├── Data/ # DTOs(Laravel Data) ├── Models/ # Eloquent Models ├── Policies/ # Policies └── Enums/ # Enums
Dependency Direction:
Presentation → Request → UseCase → Service/Repository → Model → Resource
- •下位層から上位層への依存は禁止
- •UseCase は Repository Interface 経由でアクセス
AI's Critical Weaknesses - Quick Reference
1. UseCase Structure ⚠️ MOST CRITICAL
AI gets wrong: Business logic in controller, no DTOs, or returning Eloquent Models directly
Correct pattern:
- •Input DTO (Laravel Data) for parameters
- •Output via Eloquent Model or dedicated DTO
- •Uses Repository interface (not Model directly in complex cases)
- •Class marked as
final
// ✅ Correct pattern
final class CreatePostUseCase
{
public function __construct(
private PostRepositoryInterface $postRepository,
) {}
public function execute(CreatePostData $data): Post
{
// ドメインバリデーション
$existingPost = $this->postRepository->findByUserAndWeek(
$data->userId,
$data->weekStartDate
);
if ($existingPost !== null) {
throw ValidationException::withMessages([
'week_start_date' => ['この週のレポートは既に存在します。'],
]);
}
// データ作成
return $this->postRepository->create(
$data->userId,
$data->weekStartDate,
$data->title,
$data->memo,
$data->status,
$data->tagValues
);
}
}
👉 詳細: .claude/rules/backend/02-layers.md
2. Repository Pattern ⚠️
AI gets wrong: Controller directly using Eloquent Model, or Repository returning incorrect types
Correct pattern:
- •Interface in
Repositories/directory - •Implementation in same directory or separate (for testing)
- •Returns Eloquent Model (not raw arrays)
- •Encapsulates complex queries and transactions
// ✅ Correct pattern: Interface
interface PostRepositoryInterface
{
public function findById(int $id): ?Post;
public function findByUserAndWeek(int $userId, string $weekStartDate): ?Post;
public function create(
int $userId,
string $weekStartDate,
string $title,
?string $memo,
PostStatus $status,
array $tagValues
): Post;
}
// ✅ Correct pattern: Implementation
final class PostRepository implements PostRepositoryInterface
{
public function findById(int $id): ?Post
{
return Post::find($id);
}
public function create(...): Post
{
return DB::transaction(function () use (...) {
$post = Post::create([...]);
$post->tags()->attach($tagValues);
return $post->fresh(['tags']);
});
}
}
👉 詳細: .claude/rules/backend/02-layers.md
3. DTO Design (Laravel Data) ⚠️
AI gets wrong: Not using Laravel Data, missing TypeScript generation attributes, or mutable DTOs
Correct pattern:
- •Use
spatie/laravel-data - •Add
#[TypeScript()]attribute for type generation - •All properties
readonly - •Use
#[MapName(SnakeCaseMapper::class)]for case conversion
// ✅ Correct pattern
use Spatie\LaravelData\Data;
use Spatie\LaravelData\Attributes\MapName;
use Spatie\LaravelData\Mappers\SnakeCaseMapper;
use Spatie\TypeScriptTransformer\Attributes\TypeScript;
#[TypeScript()]
#[MapName(SnakeCaseMapper::class)]
final readonly class CreatePostData extends Data
{
public function __construct(
public int $userId,
public string $weekStartDate,
#[Max(255)]
public string $title,
#[Max(1000)]
public ?string $memo,
public PostStatus $status,
/** @var array<TagValueData> */
#[DataCollectionOf(TagValueData::class)]
public array $tagValues,
) {}
}
👉 詳細: .claude/rules/backend/03-dto-data.md
4. Layer Separation ⚠️
AI gets wrong: Cross-layer dependencies (Controller → Model directly, UseCase → HTTP concerns)
Correct pattern:
- •Controller only handles HTTP, calls UseCase
- •UseCase contains business logic, uses Repository
- •Repository encapsulates data access
- •FormRequest handles validation and DTO conversion
// ✅ Correct: Controller uses UseCase only
final class PostController extends Controller
{
public function store(StorePostRequest $request): JsonResponse
{
$data = $request->getCreatePostData();
$post = $this->createPostUseCase->execute($data);
return response()->json([
'data' => new PostResource($post),
], 201);
}
}
// ✅ Correct: FormRequest converts to DTO
final class StorePostRequest extends FormRequest
{
public function rules(): array
{
return [
'title' => ['required', 'string', 'max:255'],
// ...
];
}
public function getCreatePostData(): CreatePostData
{
return CreatePostData::from([
'user_id' => auth()->id(),
'title' => $this->input('title'),
// ...
]);
}
}
👉 詳細: .claude/rules/backend/02-layers.md
5. Web vs API Controllers ⚠️
AI gets wrong: Putting dynamic data in Web Controllers, or mixing concerns
Correct pattern:
- •Web Controllers: Initial page render only, static master data
- •API Controllers: CRUD operations, dynamic data
- •Naming:
{Resource}PageControllerfor Web,{Resource}Controllerfor API
// ✅ Correct: Web Controller (static data only)
final class PostPageController extends Controller
{
public function index(Request $request): Response
{
return Inertia::render('Post/Index', [
'statusOptions' => PostStatus::toSelectArray(), // 静的データ
'filters' => $request->only(['q', 'status']),
]);
// 動的データは React 側から API 経由で取得
}
}
// ✅ Correct: API Controller (dynamic data)
final class PostController extends Controller
{
public function index(SearchPostsRequest $request): JsonResponse
{
$posts = $this->getPostsUseCase->execute($request->getSearchData());
return response()->json([
'data' => PostResource::collection($posts),
]);
}
}
👉 詳細: .claude/rules/backend/05-inertia-backend.md
Naming Conventions
| Type | Pattern | Example |
|---|---|---|
| Web Controller | {Resource}PageController | PostPageController |
| API Controller | {Resource}Controller | PostController |
| UseCase | {Action}{Resource}UseCase | CreatePostUseCase |
| Repository Interface | {Resource}RepositoryInterface | PostRepositoryInterface |
| Repository | {Resource}Repository | PostRepository |
| FormRequest (作成) | Store{Resource}Request | StorePostRequest |
| FormRequest (更新) | Update{Resource}Request | UpdatePostRequest |
| DTO | Create/Update{Resource}Data | CreatePostData |
| Resource | {Resource}Resource | PostResource |
| Service | {Resource}{Function}Service | PostExportService |
| Eloquent Model | {Resource} | Post |
| Policy | {Resource}Policy | PostPolicy |
Method Naming
| Purpose | Method Name |
|---|---|
| UseCase execution | execute |
| Find single | findById, findBy{Property} |
| Find multiple | findAll, findBy{Criteria} |
| Create | create |
| Update | update |
| Delete | delete |
| DTO conversion | get{Action}{Resource}Data |
Class Modifiers
// UseCase: final
final class CreatePostUseCase
{
public function __construct(
private PostRepositoryInterface $repository,
) {}
}
// DTO (Laravel Data): final readonly
#[TypeScript()]
final readonly class CreatePostData extends Data
{
public function __construct(
public int $userId,
// ...
) {}
}
// Repository Implementation: final
final class PostRepository implements PostRepositoryInterface {}
// Service: final
final class PostExportService {}
// Controller: NOT final (テスト時のモック作成のため)
class PostController extends Controller {}
// Model: NOT final (Laravel の仕様)
class Post extends Model {}
Prohibited Patterns
In Controller
- •❌ Business logic
- •❌ Direct Model access (
Post::where(...)) - •❌ Data transformation logic
In UseCase
- •❌ HTTP-specific logic (
Request,Response) - •❌ Direct
DB::queries (use Repository) - •❌ Returning raw arrays (return Model or DTO)
In Repository
- •❌ Business logic (only data access)
- •❌ HTTP concerns
General
- •❌
@inertiajs/reactのuseForm(Laravel Precognition を使用) - •❌ Web Controllers での動的データ提供(API 経由)
- •❌ ハードコードされた URL(Wayfinder 使用)
AI Weakness Checklist
Before considering implementation complete, verify AI didn't fall into these traps:
UseCase ⚠️ (Most Critical)
- • Controller は UseCase を呼び出すのみ(ビジネスロジックなし)
- • UseCase は Input DTO (Laravel Data) を受け取る
- • UseCase は Repository Interface を使用
- • UseCase は
finalclass - • ドメインバリデーションは UseCase 内
Repository ⚠️
- • Interface と Implementation が分離されている
- • トランザクション制御は Repository 内
- • Eloquent Model を返す(生配列ではない)
- • Implementation は
finalclass
DTO ⚠️
- •
spatie/laravel-dataを使用 - •
#[TypeScript()]attribute が付与されている - •
readonlyproperty を使用 - • FormRequest に DTO 変換メソッドがある
Layer Separation ⚠️
- • Controller は HTTP handling のみ
- • UseCase はビジネスロジックのみ
- • Repository はデータアクセスのみ
- • 下位層から上位層への依存がない
Web vs API ⚠️
- • Web Controllers は静的データのみ提供
- • 動的データは API 経由で取得
- • Web Controller は
{Resource}PageController命名
Summary: What to Watch For
AI will confidently write code that:
- •Puts business logic in Controller (should be in UseCase)
- •Accesses Model directly from Controller (should use UseCase → Repository)
- •Skips DTO conversion in FormRequest
- •Missing TypeScript generation attributes on DTOs
- •Provides dynamic data in Web Controllers (should use API)
Trust AI for:
- •Basic PHP syntax
- •Simple CRUD operations
- •Controller routing
Scrutinize AI for:
- •UseCase structure (DTO required)
- •Repository pattern (Interface + Implementation)
- •Layer boundaries (no cross-layer dependencies)
- •Web vs API Controller responsibility
When in doubt, ask: "Is each layer handling only its responsibility?"