API層の命名規則
概要
将来のAPI化を見据え、API層の命名規則と実装方針を定める。現在はLivewire + Alpine.jsでフロントエンドを実装しているが、将来的にNextJS + React(Headless)との並立を想定し、API層の設計方針を明確化する。
基本方針
- Laravel標準の
JsonResourceを継承したクラスを使用 - Filament Resourceとの名前衝突を避けるため、サフィックス方式を採用
- サービス層の分離を徹底し、将来のAPI化コストを最小化
API Resourceの命名規則
命名規則
- 形式:
{Model}ApiResource - 例:
ProductApiResourceOrderApiResourceCategoryApiResourceCustomerApiResource
配置場所
app/Http/Resources/
├── ProductApiResource.php
├── OrderApiResource.php
├── CategoryApiResource.php
└── ...
実装例
<?php
declare(strict_types=1);
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class ProductApiResource extends JsonResource
{
public function toArray($request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'slug' => $this->slug,
'price' => $this->price,
'description' => $this->description,
'image' => $this->image,
'categories' => CategoryApiResource::collection($this->whenLoaded('categories')),
'brand' => new BrandApiResource($this->whenLoaded('brand')),
'created_at' => $this->created_at?->toIso8601String(),
'updated_at' => $this->updated_at?->toIso8601String(),
];
}
}
Filament Resourceとの区別
命名規則の比較
| 種別 | 命名規則 | 配置場所 | 継承クラス | 用途 |
|---|---|---|---|---|
| Filament Resource | {Model}Resource |
app/Admin/Resources/{Model}Resource.php |
Filament\Resources\Resource |
管理画面のUI定義(フォーム、テーブル) |
| API Resource | {Model}ApiResource |
app/Http/Resources/{Model}ApiResource.php |
Illuminate\Http\Resources\Json\JsonResource |
APIレスポンスのJSON形式定義 |
重要な注意事項
- API Resource(
JsonResource)とFilament Resource(Filament\Resources\Resource)は完全に別物 - 互いに影響しない(現在の管理画面への影響は一切ない)
- 名前空間が異なるため、同じディレクトリに配置しても問題ない
将来の拡張(API ファサード別の分離)
将来的にAPI ファサード(Public/Partner/Admin)を分離する場合は、以下の構成も検討可能:
app/Http/Resources/
├── Public/
│ ├── ProductApiResource.php // 公開API用
│ └── OrderApiResource.php
├── Partner/
│ ├── ProductApiResource.php // パートナーAPI用
│ └── OrderApiResource.php
└── Admin/
├── ProductApiResource.php // 管理API用
└── OrderApiResource.php
この場合、各ファサードで異なるレスポンス形式を定義できる。
実装方針
Phase 1: 現在(Livewire + Alpine.js)
- API Resourceは未実装
- サービス層を分離して実装することで、将来のAPI化を容易にする
- 例:
CartService,ProductCatalogServiceなど
// サービス層の分離例
app/Services/CartService.php
app/Services/Catalog/ProductCatalogService.php
// Livewireコンポーネントは薄く保つ
class CartPage extends Component
{
public function addToCart(int $productId): void
{
app(CartService::class)->addToCart($productId);
$this->dispatch('cart-updated');
}
}
Phase 2: 将来(API化)
- 既存のサービス層を活用してAPI ResourceとAPI Controllerを作成
- Livewire版とAPI版を共存可能に設計
// API Controller例
app/Http/Controllers/Api/ProductApiController.php
class ProductApiController extends Controller
{
public function index(Request $request)
{
$products = app(ProductCatalogService::class)->getActiveProducts();
return ProductApiResource::collection($products);
}
}
サービス層の分離原則
将来のAPI化を見据え、以下の原則を守る:
✅ 推奨:サービス層にロジックを集約
// ✅ 良い例:サービス層にロジックを集約
class CartService
{
public function addToCart(int $productId, int $quantity = 1): void
{
// ビジネスロジック
}
}
// Livewireコンポーネントは薄く保つ
class CartPage extends Component
{
public function addToCart(int $productId): void
{
app(CartService::class)->addToCart($productId);
}
}
❌ 非推奨:Livewireコンポーネントに直接ロジックを書く
// ❌ 避けるべき:Livewireコンポーネントに直接ロジックを書く
class CartPage extends Component
{
public function addToCart(int $productId): void
{
// ロジックを直接書かない
$cartItems = session()->get('cart_items', []);
// ...
}
}
認証・認可の統一
Livewire版とAPI版で同じ認証・認可ロジックを使えるように:
// app/Policies/ProductPolicy.php(既存を活用)
class ProductPolicy
{
public function viewAny(User $user): bool
{
// Livewire版とAPI版で共通
}
}
データアクセス層の抽象化
リポジトリパターンやEloquentのクエリを共通化:
// app/Repositories/ProductRepository.php(将来作成)
class ProductRepository
{
public function getActiveProducts(): Collection
{
return Product::where('is_active', true)
->with(['categories', 'brand'])
->get();
}
}
まとめ
- API Resourceの命名:
{Model}ApiResource(サフィックス方式) - 配置場所:
app/Http/Resources/{Model}ApiResource.php - 現在の実装: サービス層の分離を徹底
- 将来の実装: 既存のサービス層を活用してAPI化
- 管理画面への影響: 一切なし(Filament Resourceとは完全に独立)
この方針により、現在のLivewire + Alpine.js実装と、将来のNextJS + React(Headless)の並立をスムーズに行える。