Chuyển đến nội dung chính

レッスン 20: クリーンなアーキテクチャとパッケージ

リポジトリ パターン、サービス層、アクション クラス。ドメイン駆動設計の基本。 Laravelパッケージの作成。モジュールベースの構造 (nwidart/laravel-modules)。堅実な原則。

💻 プログラミング — レッスン 20 レッスン 20: クリーンなアーキテクチャとパッケージ

Laravel: 基本から上級まで

パート 6: テスト、Docker、本番環境

xdev.asia

1. サービス層

namespace App\Services;

class ProductService
{
    public function __construct(
        private readonly ProductRepository $repository,
    ) {}

    public function create(array $data): Product
    {
        $product = $this->repository->create($data);

        if (isset($data['tags'])) {
            $product->tags()->sync($data['tags']);
        }

        event(new ProductCreated($product));
        Cache::tags(['products'])->flush();

        return $product;
    }
}

// Controller
class ProductController extends Controller
{
    public function store(StoreProductRequest $request, ProductService $service)
    {
        $product = $service->create($request->validated());
        return new ProductResource($product);
    }
}

2. アクションクラス

namespace App\Actions;

class CreateOrder
{
    public function __construct(
        private readonly PaymentService $payment,
    ) {}

    public function execute(User $user, array $items): Order
    {
        return DB::transaction(function () use ($user, $items) {
            $order = $user->orders()->create([
                'total' => collect($items)->sum(fn ($i) => $i['price'] * $i['quantity']),
            ]);

            foreach ($items as $item) {
                $order->items()->create($item);
                Product::where('id', $item['product_id'])->decrement('stock', $item['quantity']);
            }

            $this->payment->charge($order);
            return $order;
        });
    }
}

3. リポジトリパターン

interface ProductRepositoryInterface
{
    public function findBySlug(string $slug): ?Product;
    public function getActive(int $perPage = 20): LengthAwarePaginator;
}

class EloquentProductRepository implements ProductRepositoryInterface
{
    public function findBySlug(string $slug): ?Product
    {
        return Product::with('category')->where('slug', $slug)->first();
    }

    public function getActive(int $perPage = 20): LengthAwarePaginator
    {
        return Product::where('is_active', true)->paginate($perPage);
    }
}

// AppServiceProvider
$this->app->bind(ProductRepositoryInterface::class, EloquentProductRepository::class);

4. モジュールベースの構造

app/
├── Modules/
│   ├── Product/
│   │   ├── Actions/
│   │   ├── Models/
│   │   ├── Services/
│   │   ├── Http/Controllers/
│   │   ├── Http/Requests/
│   │   ├── Http/Resources/
│   │   └── routes.php
│   ├── Order/
│   └── User/

次の記事: Docker と CI/CD。