Laravel – Architektura Warstwowa (Cheat Sheet)

MODELE (Eloquent)

REPOZYTORIA

INTERFEJSY

SERWISY

DTO (Data Transfer Object)

FORM REQUEST (Http\Requests)

KONTROLERY

OPERACJE MASOWE

Dobre praktyki

Testowanie – Co testować?

Warstwa Testujemy
DTO Poprawność danych i strukturę pól
Model Scope'y, relacje, casty
Request Walidację, błędy, komunikaty
Kontroler Statusy odpowiedzi, flow danych
Serwis Logika biznesowa, mock repo
Repozytorium CRUD, filtrowanie, zapytania
Interfejs Bindowanie i kontrakt

Przykład: DTO + Serwis + Test

// DTO
  final class CreateUserDto {
    public function __construct(
      public readonly string $name,
      public readonly string $email
    ) {}
  }
  
  // Serwis
  class UserService {
    public function __construct(private UserRepository $repo) {}
    public function create(CreateUserDto $dto): User {
      return $this->repo->create([
        'name' => $dto->name,
        'email' => $dto->email,
      ]);
    }
  }

Test jednostkowy: Kontroler

public function test_controller_returns_json_response() {
        $dto = new CreateUserDto('Jan', 'jan@example.com');
        $service = Mockery::mock(UserService::class);
        $service->shouldReceive('create')->once()->andReturn(new User());
      
        $this->app->instance(UserService::class, $service);
      
        $response = $this->postJson('/api/users', [
          'name' => 'Jan', 'email' => 'jan@example.com'
        ]);
      
        $response->assertStatus(201);
      }

Test jednostkowy: Serwis

public function test_creates_user() {
    $repo = Mockery::mock(UserRepository::class);
    $dto = new CreateUserDto('Jan', 'jan@example.com');
    $repo->shouldReceive('create')->once()->with([
      'name' => 'Jan', 'email' => 'jan@example.com'
    ])->andReturn(new User());
  
    $service = new UserService($repo);
    $this->assertInstanceOf(User::class, $service->create($dto));
  }

Test jednostkowy: FormRequest

public function test_rules_are_valid() {
    $request = new StoreUserRequest();
    $rules = $request->rules();
  
    $this->assertArrayHasKey('email', $rules);
    $this->assertStringContainsString('required', $rules['email']);
  }

Test jednostkowy: Model

public function test_user_has_posts_relation() {
    $user = new User();
    $this->assertInstanceOf(HasMany::class, $user->posts());
  }

Test jednostkowy: Event & Listener

public function test_event_is_dispatched() {
    Event::fake();
  
    event(new UserCreatedEvent($user));
  
    Event::assertDispatched(UserCreatedEvent::class);
  }

Struktura katalogów (przykład)

app/
  ├── DTO/
  │   └── CreateUserDto.php
  ├── Events/
  │   └── UserCreatedEvent.php
  ├── Http/
  │   ├── Controllers/
  │   │   └── UserController.php
  │   ├── Requests/
  │   │   └── StoreUserRequest.php
  ├── Interfaces/
  │   └── UserRepositoryInterface.php
  ├── Listeners/
  │   └── SendWelcomeEmail.php
  ├── Models/
  │   └── User.php
  ├── Repositories/
  │   └── UserRepository.php
  ├── Services/
  │   └── UserService.php
  └── Providers/
      └── AppServiceProvider.php

SŁOWNICZEK POJĘĆ