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

Lesson 20: Clean Architecture & Packages

Repository pattern, Service layer, Action classes. Domain-Driven Design basics. Creating Laravel packages. Module-based structure (nwidart/laravel-modules). SOLID principles.

💻 Programming — Lesson 20 Lesson 20: Clean Architecture & Packages

Laravel: From Basics to Advanced

Part 6: Testing, Docker & Production

xdev.asia

1. Service Layer

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. Action Classes

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. Repository Pattern

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. Module-based Structure

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

Next article: Docker & CI/CD.