Soluções Laravel: Padrões Avançados, Depuração e Perguntas de Entrevista 2026

Dominar soluções Laravel com padrões arquiteturais avançados, técnicas de depuração e conhecimentos essenciais para entrevistas. Classes de serviço, padrão Repository, depuração com Telescope e perguntas frequentes.

Padrões arquiteturais e depuração avançada em Laravel com PHP 8.4

As soluções Laravel para aplicações em produção exigem muito mais do que operações CRUD básicas. A diferença entre um desenvolvedor Laravel júnior e sênior se manifesta na estruturação do código, nos métodos de depuração e na capacidade de responder perguntas arquiteturais durante entrevistas técnicas.

O que os recrutadores procuram

As vagas sênior de Laravel esperam que os candidatos expliquem o Service Container, demonstrem fluxos de trabalho de depuração com Telescope ou Debugbar, e justifiquem quando usar padrões como Repository versus consultas Eloquent diretas.

Classes de Serviço: Extraindo Lógica de Negócio dos Controllers

Os controllers no Laravel lidam com preocupações HTTP: receber requisições, validar entradas e retornar respostas. A lógica de negócio pertence a classes de serviço dedicadas, tornando o código testável e reutilizável através de controllers, comandos e jobs.

app/Services/OrderService.phpphp
// Service class encapsulating order business logic

namespace App\Services;

use App\Models\Order;
use App\Models\User;
use App\Exceptions\InsufficientStockException;
use App\Jobs\SendOrderConfirmationEmail;
use Illuminate\Support\Facades\DB;

class OrderService
{
    public function __construct(
        private InventoryService $inventory,
        private PaymentService $payment
    ) {}

    /**
     * Create an order with full validation and side effects
     *
     * @throws InsufficientStockException
     */
    public function createOrder(User $user, array $items, string $paymentMethod): Order
    {
        // Validate stock availability before starting transaction
        foreach ($items as $item) {
            if (!$this->inventory->hasStock($item['product_id'], $item['quantity'])) {
                throw new InsufficientStockException($item['product_id']);
            }
        }

        return DB::transaction(function () use ($user, $items, $paymentMethod) {
            // Create order record
            $order = $user->orders()->create([
                'status' => 'pending',
                'total' => $this->calculateTotal($items),
            ]);

            // Attach order items
            foreach ($items as $item) {
                $order->items()->create($item);
                $this->inventory->decrementStock($item['product_id'], $item['quantity']);
            }

            // Process payment
            $this->payment->charge($user, $order->total, $paymentMethod);
            $order->update(['status' => 'paid']);

            // Queue confirmation email
            SendOrderConfirmationEmail::dispatch($order);

            return $order;
        });
    }

    private function calculateTotal(array $items): int
    {
        return collect($items)->sum(fn($item) => $item['price'] * $item['quantity']);
    }
}
app/Http/Controllers/OrderController.phpphp
// Controller delegating to service class

namespace App\Http\Controllers;

use App\Http\Requests\CreateOrderRequest;
use App\Services\OrderService;
use Illuminate\Http\JsonResponse;

class OrderController extends Controller
{
    public function __construct(
        private OrderService $orderService
    ) {}

    public function store(CreateOrderRequest $request): JsonResponse
    {
        $order = $this->orderService->createOrder(
            $request->user(),
            $request->validated('items'),
            $request->validated('payment_method')
        );

        return response()->json(['order_id' => $order->id], 201);
    }
}

As classes de serviço podem ser injetadas em qualquer lugar do Laravel: controllers, comandos, outros serviços ou classes de job. A documentação do Service Container cobre a resolução automática e os bindings.

Padrão Repository: Quando Eloquent Direto Não É Suficiente

O padrão Repository adiciona uma camada de abstração entre a lógica de negócio e o acesso a dados. Este padrão se mostra valioso quando as aplicações precisam trocar fontes de dados, cachear resultados de consultas de forma transparente, ou isolar lógica de consultas complexas.

Contexto de entrevista

Os candidatos são frequentemente perguntados se usam o padrão Repository com Laravel. A resposta correta depende do contexto: repositórios agregam valor para aplicações grandes com requisitos de dados complexos, mas criam abstração desnecessária para aplicações CRUD simples.

app/Repositories/Contracts/UserRepositoryInterface.phpphp
// Interface defining the contract

namespace App\Repositories\Contracts;

use App\Models\User;
use Illuminate\Support\Collection;

interface UserRepositoryInterface
{
    public function findById(int $id): ?User;
    public function findByEmail(string $email): ?User;
    public function getActiveUsers(): Collection;
    public function getUsersWithRecentOrders(int $days = 30): Collection;
    public function create(array $data): User;
    public function update(User $user, array $data): User;
}
app/Repositories/EloquentUserRepository.phpphp
// Eloquent implementation of the repository

namespace App\Repositories;

use App\Models\User;
use App\Repositories\Contracts\UserRepositoryInterface;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache;

class EloquentUserRepository implements UserRepositoryInterface
{
    public function findById(int $id): ?User
    {
        return Cache::remember(
            "user.{$id}",
            now()->addMinutes(10),
            fn() => User::find($id)
        );
    }

    public function findByEmail(string $email): ?User
    {
        return User::where('email', $email)->first();
    }

    public function getActiveUsers(): Collection
    {
        return User::where('status', 'active')
            ->orderBy('name')
            ->get();
    }

    public function getUsersWithRecentOrders(int $days = 30): Collection
    {
        return User::whereHas('orders', function ($query) use ($days) {
            $query->where('created_at', '>=', now()->subDays($days));
        })
        ->with(['orders' => fn($q) => $q->latest()->limit(5)])
        ->get();
    }

    public function create(array $data): User
    {
        $user = User::create($data);
        Cache::forget("user.{$user->id}");
        return $user;
    }

    public function update(User $user, array $data): User
    {
        $user->update($data);
        Cache::forget("user.{$user->id}");
        return $user->fresh();
    }
}
app/Providers/RepositoryServiceProvider.phpphp
// Binding interface to implementation

namespace App\Providers;

use App\Repositories\Contracts\UserRepositoryInterface;
use App\Repositories\EloquentUserRepository;
use Illuminate\Support\ServiceProvider;

class RepositoryServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->app->bind(
            UserRepositoryInterface::class,
            EloquentUserRepository::class
        );
    }
}

A implementação do repositório gerencia o cache internamente, permitindo que as classes de serviço se concentrem nas regras de negócio em vez da invalidação de cache.

Pronto para mandar bem nas entrevistas de Laravel?

Pratique com nossos simuladores interativos, flashcards e testes tecnicos.

Depuração de Aplicações Laravel com Telescope

Laravel Telescope fornece um assistente de depuração que registra requisições, exceções, consultas de banco de dados, jobs e muito mais. A depuração em produção requer entender quais métricas importam e como filtrar o ruído.

config/telescope.phpphp
// Telescope configuration for different environments

return [
    'enabled' => env('TELESCOPE_ENABLED', true),

    // Only record slow queries in production
    'query' => [
        'slow' => env('TELESCOPE_SLOW_QUERY_THRESHOLD', 100), // milliseconds
    ],

    // Prune old entries to manage database size
    'storage' => [
        'database' => [
            'connection' => env('DB_CONNECTION', 'mysql'),
            'chunk' => 1000,
        ],
    ],
];
app/Providers/TelescopeServiceProvider.phpphp
// Custom filtering for Telescope entries

namespace App\Providers;

use Laravel\Telescope\IncomingEntry;
use Laravel\Telescope\Telescope;
use Laravel\Telescope\TelescopeApplicationServiceProvider;

class TelescopeServiceProvider extends TelescopeApplicationServiceProvider
{
    public function register(): void
    {
        Telescope::night();

        $this->hideSensitiveRequestDetails();

        // Filter out noise in production
        Telescope::filter(function (IncomingEntry $entry) {
            // Always record exceptions and slow queries
            if ($entry->isException()) {
                return true;
            }

            if ($entry->isSlowQuery()) {
                return true;
            }

            // Skip health check endpoints
            if ($entry->isRequest() && str_starts_with($entry->content['uri'] ?? '', '/health')) {
                return false;
            }

            // Skip static asset requests
            if ($entry->isRequest() && preg_match('/\.(css|js|png|jpg|svg)$/', $entry->content['uri'] ?? '')) {
                return false;
            }

            // Record everything in local environment
            if ($this->app->environment('local')) {
                return true;
            }

            // Production: only record failures and slow operations
            return $entry->isFailedRequest()
                || $entry->isFailedJob()
                || $entry->hasMonitoredTag();
        });
    }

    protected function hideSensitiveRequestDetails(): void
    {
        // Hide sensitive data from Telescope UI
        Telescope::hideRequestParameters(['password', 'password_confirmation', 'token']);
        Telescope::hideRequestHeaders(['authorization', 'cookie']);
    }
}

O método isSlowQuery() marca as consultas de banco de dados que excedem o limite configurado. A análise de consultas lentas revela índices ausentes e problemas N+1 que ferramentas de profiling como Debugbar também detectam.

Perguntas de Entrevista Comuns e Respostas Sólidas

As entrevistas técnicas para vagas de Laravel seguem padrões. As perguntas abaixo aparecem frequentemente, e as respostas demonstram a profundidade que os entrevistadores esperam.

O Que É o Service Container e Por Que É Importante?

O Service Container é o container de injeção de dependências do Laravel. Ele gerencia as dependências das classes e realiza a injeção de dependências automaticamente. Quando um construtor de controller tem um type-hint de OrderService, o container resolve e injeta uma instância.

php
// Automatic resolution: container reads type hints and builds dependencies
public function __construct(OrderService $service)
{
    // $service is automatically instantiated and injected
}

// Manual binding for interfaces or complex setup
$this->app->bind(PaymentGateway::class, function ($app) {
    return new StripeGateway(
        config('services.stripe.key'),
        config('services.stripe.secret')
    );
});

// Singleton: same instance throughout the request
$this->app->singleton(MetricsCollector::class, function ($app) {
    return new MetricsCollector(
        $app->make(Cache::class)
    );
});

O container permite um acoplamento fraco: as classes dependem de abstrações (interfaces) em vez de implementações concretas, tornando os testes e a troca de implementações simples.

Como o Laravel Gerencia Transações de Banco de Dados?

O Laravel encapsula as operações de banco de dados em transações usando o método DB::transaction(). As transações garantem a atomicidade: ou todas as operações são bem-sucedidas, ou tudo é revertido.

php
// Simple transaction with automatic rollback on exception
DB::transaction(function () {
    $user = User::create(['email' => 'test@example.com']);
    $user->profile()->create(['bio' => 'New user']);
    // If profile creation fails, user creation rolls back
});

// Manual transaction control for complex flows
DB::beginTransaction();

try {
    $order = Order::create($data);
    PaymentGateway::charge($order->total);

    DB::commit();
} catch (PaymentFailedException $e) {
    DB::rollBack();
    throw $e;
}

// Nested transactions use savepoints
DB::transaction(function () {
    User::create(['email' => 'outer@test.com']);

    DB::transaction(function () {
        // This creates a savepoint
        Profile::create(['user_id' => 1]);
    });
    // Inner failure only rolls back to savepoint
});

Os entrevistadores frequentemente perguntam em seguida sobre deadlocks. A resposta: o Laravel tenta novamente as transações que falham devido a deadlocks (configurável através do segundo argumento de DB::transaction()).

Explique os Middleware e Forneça um Caso de Uso Real

Os Middleware filtram as requisições HTTP que entram na aplicação. Cada middleware pode inspecionar, modificar ou rejeitar requisições antes que cheguem aos controllers.

app/Http/Middleware/EnsureTeamMember.phpphp
// Custom middleware checking team membership

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class EnsureTeamMember
{
    public function handle(Request $request, Closure $next, string $role = 'member'): Response
    {
        $team = $request->route('team');

        if (!$request->user()->belongsToTeam($team)) {
            abort(403, 'You are not a member of this team.');
        }

        if ($role === 'admin' && !$request->user()->isTeamAdmin($team)) {
            abort(403, 'Admin access required.');
        }

        return $next($request);
    }
}
routes/web.phpphp
// Applying middleware to routes

Route::middleware(['auth', 'team.member:admin'])->group(function () {
    Route::get('/teams/{team}/settings', [TeamController::class, 'settings']);
    Route::put('/teams/{team}/settings', [TeamController::class, 'updateSettings']);
});

Os middleware são executados em ordem. O middleware de autenticação deve ser executado antes do middleware de autorização, e o middleware de logging tipicamente envolve tudo.

Resolvendo o Problema de Consultas N+1

O problema N+1 gera uma consulta para a coleção inicial mais uma consulta por item ao acessar os relacionamentos. Uma lista de 100 artigos com autores produz 101 consultas em vez de 2.

php
// Problem: 101 queries for 100 articles
$articles = Article::all();
foreach ($articles as $article) {
    echo $article->author->name; // Each access triggers a query
}

// Solution: eager load with 2 queries total
$articles = Article::with('author')->get();
foreach ($articles as $article) {
    echo $article->author->name; // No additional queries
}

// Nested eager loading for complex relationships
$articles = Article::with([
    'author.profile',
    'comments' => fn($query) => $query->latest()->limit(5),
    'comments.user',
    'tags',
])->get();

O Laravel pode prevenir o lazy loading em desenvolvimento para detectar problemas N+1 precocemente:

app/Providers/AppServiceProvider.phpphp
use Illuminate\Database\Eloquent\Model;

public function boot(): void
{
    Model::preventLazyLoading(!$this->app->isProduction());
}

Com esta configuração, acessar um relacionamento não carregado lança uma exceção em desenvolvimento, forçando um eager loading explícito.

Pronto para mandar bem nas entrevistas de Laravel?

Pratique com nossos simuladores interativos, flashcards e testes tecnicos.

Classes Action para Operações de Propósito Único

As classes Action encapsulam operações únicas que podem ser invocadas de múltiplos pontos de entrada. Diferentemente das classes de serviço que agrupam métodos relacionados, as actions lidam com uma única tarefa com entradas e saídas claras.

app/Actions/CreateUserAction.phpphp
// Single-purpose action class

namespace App\Actions;

use App\Models\User;
use App\Notifications\WelcomeNotification;
use Illuminate\Support\Facades\Hash;

class CreateUserAction
{
    public function execute(array $data): User
    {
        $user = User::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => Hash::make($data['password']),
        ]);

        $user->notify(new WelcomeNotification());

        return $user;
    }
}
php
// Usage in controller
public function store(RegisterRequest $request, CreateUserAction $action)
{
    $user = $action->execute($request->validated());
    return redirect()->route('dashboard');
}

// Usage in command
public function handle(CreateUserAction $action)
{
    $user = $action->execute([
        'name' => $this->argument('name'),
        'email' => $this->argument('email'),
        'password' => $this->argument('password'),
    ]);
    $this->info("Created user: {$user->id}");
}

// Usage in test
public function test_user_creation(): void
{
    $action = new CreateUserAction();
    $user = $action->execute([
        'name' => 'Test User',
        'email' => 'test@example.com',
        'password' => 'password',
    ]);

    $this->assertDatabaseHas('users', ['email' => 'test@example.com']);
}

As classes Action funcionam bem com o sistema de jobs do Laravel: a action contém a lógica, e o job gerencia o enfileiramento e o comportamento de retry.

Arquitetura Orientada a Eventos com Events e Listeners

Os eventos desacoplam o momento em que algo acontece das reações a esse evento. Quando um pedido é realizado, o evento OrderPlaced é disparado. Os listeners cuidam do envio de emails, atualização de analytics e notificação de armazéns independentemente.

app/Events/OrderPlaced.phpphp
// Event class carrying order data

namespace App\Events;

use App\Models\Order;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class OrderPlaced
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public function __construct(
        public Order $order
    ) {}
}
app/Listeners/SendOrderConfirmation.phpphp
// Queued listener for email

namespace App\Listeners;

use App\Events\OrderPlaced;
use App\Notifications\OrderConfirmationNotification;
use Illuminate\Contracts\Queue\ShouldQueue;

class SendOrderConfirmation implements ShouldQueue
{
    public function handle(OrderPlaced $event): void
    {
        $event->order->user->notify(
            new OrderConfirmationNotification($event->order)
        );
    }
}
app/Listeners/UpdateInventoryAnalytics.phpphp
// Another listener for the same event

namespace App\Listeners;

use App\Events\OrderPlaced;
use App\Services\AnalyticsService;
use Illuminate\Contracts\Queue\ShouldQueue;

class UpdateInventoryAnalytics implements ShouldQueue
{
    public function __construct(
        private AnalyticsService $analytics
    ) {}

    public function handle(OrderPlaced $event): void
    {
        foreach ($event->order->items as $item) {
            $this->analytics->recordSale(
                $item->product_id,
                $item->quantity,
                $event->order->created_at
            );
        }
    }
}
php
// Dispatching the event
OrderPlaced::dispatch($order);

// Or using the event helper
event(new OrderPlaced($order));

Os listeners que implementam ShouldQueue são executados de forma assíncrona, evitando que operações lentas bloqueiem a resposta HTTP.

Pontos Chave para Desenvolvedores Laravel

  • As classes de serviço extraem a lógica de negócio dos controllers, melhorando a testabilidade e permitindo reutilização através de contextos HTTP, CLI e de fila
  • O padrão Repository agrega valor quando as aplicações precisam de cache, abstração de fonte de dados ou encapsulação de consultas complexas, mas cria sobrecarga para CRUD simples
  • Os filtros do Telescope reduzem o ruído em produção ao registrar apenas exceções, consultas lentas e operações com falha
  • O Service Container gerencia a injeção de dependências automaticamente através de type hints, com bindings explícitos para interfaces e configurações complexas
  • Os problemas N+1 desaparecem com eager loading via with(), e Model::preventLazyLoading() detecta eager loads ausentes durante o desenvolvimento
  • As classes Action lidam com operações de propósito único invocáveis de controllers, comandos, testes e jobs
  • Os eventos desacoplam "o que aconteceu" de "o que deveria acontecer em seguida", com listeners em fila gerenciando os efeitos colaterais de forma assíncrona
  • As respostas em entrevistas devem demonstrar compreensão dos trade-offs: quando os padrões ajudam versus quando adicionam complexidade desnecessária

Comece a praticar!

Teste seus conhecimentos com nossos simuladores de entrevista e testes tecnicos.

Desafio do dia

Você saberia encontrar o bug em Laravel?

Um trecho real, um bug escondido, uma tentativa por dia. Sem conta para testar.

Anthony Fillion-Maillet

Escrito por

Anthony Fillion-Maillet

Fundador da SharpSkill

Desenvolvedor fullstack há mais de 10 anos. Dirige a SharpSkill e responde por tudo o que é publicado aqui.

Atualizado em 25 de agosto de 2026

Tags

#laravel
#php
#debugging
#design-patterns
#interview

Compartilhar

Artigos relacionados