2025-12-07
#architecture
#clean-code
#backend

Clean Architecture Lifecycle

Responsibilities of Presentation, Application, Domain, and Infrastructure layers.

The project architecture is built on strict separation of layer responsibilities. Data flow is unidirectional: from the external world (Controller) to the center (Entity), with dependencies pointing inward.

Layer Structure

1. Presentation Layer (Controllers)

Responsibility: Receive HTTP request, validate data format, delegate control to UseCase.

  • Characteristics: Maximally "thin". Contains no business logic.
  • Input: Http Request.
  • Output: Response (via RestResponseFactory).

2. Application Layer (Use Cases)

Responsibility: Orchestration of business scenarios.

  • Characteristics: Does not make business rule decisions (if/else), but manages the flow: "Fetch -> Modify -> Save -> Notify".
  • Input: Strictly typed DTO.
  • Output: Domain Entity or DTO.

3. Domain Layer (Entities, Services)

Responsibility: Business logic, invariants, state validation rules.

  • Characteristics: Pure PHP code, independent of framework or database.
  • Components: Entities, Value Objects, Domain Services, Factories.

4. Infrastructure Layer (Repositories, Adapters)

Responsibility: Implementation of interfaces (ports), database operations, external APIs.

  • Characteristics: Technical implementation details (Eloquent, Redis, Mailgun).

Data Flow Diagram

text
Client          Controller        UseCase         Repository        Database
  │                 │                │                │                │
  │ POST /api/..    │                │                │                │
  │────────────────▶│                │                │                │
  │                 │ execute(DTO)   │                │                │
  │                 │───────────────▶│                │                │
  │                 │                │ findById(id)   │                │
  │                 │                │───────────────▶│                │
  │                 │                │                │ SELECT ...     │
  │                 │                │                │───────────────▶│
  │                 │                │                │      Row       │
  │                 │                │ Entity         │◀───────────────│
  │                 │                │◀───────────────│                │
  │                 │                │                │                │
  │                 │                │ [Update Logic] │                │
  │                 │                │ (Invariants)   │                │
  │                 │                │                │                │
  │                 │                │ save(entity)   │                │
  │                 │                │───────────────▶│                │
  │                 │                │                │ UPDATE ...     │
  │                 │                │                │───────────────▶│
  │                 │                │                │                │
  │                 │    Entity      │                │                │
  │                 │◀───────────────│                │                │
  │ JSON Response   │                │                │                │
  │◀────────────────│                │                │                │
  │                 │                │                │                │

Implementation Example (Cancel Reservation)

Controller

php
readonly class ReservationCancelController
{
    public function __construct(
        private ReservationCancelUseCase $useCase,
        private RestResponseFactory $responseFactory
    ) {}

    public function __invoke(string $id): Response
    {
        // Errors (EntityNotFound, BusinessException) are caught globally
        $reservation = $this->useCase->execute($id);

        return $this->responseFactory->ok(new ReservationResource($reservation));
    }
}

UseCase

php
readonly class ReservationCancelUseCase
{
    public function __construct(
        private ReservationRepository $repository
    ) {}

    public function execute(string $id): Reservation
    {
        // 1. Find (Fail Fast - throws ModelNotFoundException)
        $reservation = $this->repository->findById($id);

        // 2. Business Logic (Rich Model - throws BusinessException if invalid)
        $reservation->cancel(); 

        // 3. Persistence (Fail Fast - throws EntitySaveException)
        $this->repository->save($reservation);

        // 4. Side Effects (Events)
        ReservationCancelled::dispatch($reservation);

        return $reservation;
    }
}

Connected Thoughts

Egor Zdioruc | Lead Full Stack Developer | Laravel & AI Solutions