# Soluciones Laravel: Patrones Avanzados, Depuración y Preguntas de Entrevista 2026 > Dominar las soluciones Laravel con patrones arquitectónicos avanzados, técnicas de depuración y conocimientos esenciales para entrevistas. Clases de servicio, patrón Repository, depuración con Telescope y preguntas frecuentes. - Published: 2026-08-25 - Updated: 2026-08-25 - Author: Anthony Fillion-Maillet - Tags: laravel, php, debugging, design-patterns, interview - Reading time: 12 min --- Las soluciones Laravel para aplicaciones en producción requieren mucho más que operaciones CRUD básicas. La diferencia entre un desarrollador Laravel junior y senior se manifiesta en la estructuración del código, los métodos de depuración y la capacidad de responder preguntas arquitectónicas durante las entrevistas técnicas. > **Lo que buscan los reclutadores** > > Los puestos senior de Laravel esperan que los candidatos expliquen el Service Container, demuestren flujos de trabajo de depuración con Telescope o Debugbar, y justifiquen cuándo usar patrones como Repository versus consultas Eloquent directas. ## Clases de Servicio: Extraer la Lógica de Negocio de los Controladores Los controladores en Laravel manejan las preocupaciones HTTP: recibir solicitudes, validar entradas y devolver respuestas. La lógica de negocio pertenece a clases de servicio dedicadas, haciendo el código testeable y reutilizable a través de controladores, comandos y jobs. ```php // app/Services/OrderService.php // 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']); } } ``` ```php // app/Http/Controllers/OrderController.php // 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); } } ``` Las clases de servicio pueden inyectarse en cualquier lugar de Laravel: controladores, comandos, otros servicios o clases de job. La [documentación del Service Container](https://laravel.com/docs/11.x/container) cubre la resolución automática y los bindings. ## Patrón Repository: Cuando Eloquent Directo No Es Suficiente El patrón Repository agrega una capa de abstracción entre la lógica de negocio y el acceso a datos. Este patrón resulta valioso cuando las aplicaciones necesitan cambiar fuentes de datos, cachear resultados de consultas de manera transparente, o aislar lógica de consultas complejas. > **Contexto de entrevista** > > A los candidatos se les pregunta frecuentemente si usan el patrón Repository con Laravel. La respuesta correcta depende del contexto: los repositorios aportan valor para aplicaciones grandes con requisitos de datos complejos, pero crean abstracción innecesaria para aplicaciones CRUD simples. ```php // app/Repositories/Contracts/UserRepositoryInterface.php // 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; } ``` ```php // app/Repositories/EloquentUserRepository.php // 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(); } } ``` ```php // app/Providers/RepositoryServiceProvider.php // 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 ); } } ``` La implementación del repositorio maneja el caché internamente, permitiendo que las clases de servicio se enfoquen en las reglas de negocio en lugar de la invalidación de caché. ## Depuración de Aplicaciones Laravel con Telescope [Laravel Telescope](https://laravel.com/docs/11.x/telescope) proporciona un asistente de depuración que registra solicitudes, excepciones, consultas de base de datos, jobs y más. La depuración en producción requiere entender qué métricas importan y cómo filtrar el ruido. ```php // config/telescope.php // 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, ], ], ]; ``` ```php // app/Providers/TelescopeServiceProvider.php // 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']); } } ``` El método `isSlowQuery()` marca las consultas de base de datos que exceden el umbral configurado. El análisis de consultas lentas revela índices faltantes y problemas N+1 que herramientas de profiling como [Debugbar](https://github.com/barryvdh/laravel-debugbar) también detectan. ## Preguntas de Entrevista Comunes y Respuestas Sólidas Las entrevistas técnicas para puestos de Laravel siguen patrones. Las preguntas a continuación aparecen frecuentemente, y las respuestas demuestran la profundidad que los entrevistadores esperan. ### ¿Qué Es el Service Container y Por Qué Es Importante? El Service Container es el [contenedor de inyección de dependencias](https://laravel.com/docs/11.x/container) de Laravel. Gestiona las dependencias de las clases y realiza la inyección de dependencias automáticamente. Cuando un constructor de controlador tiene un type-hint de `OrderService`, el contenedor resuelve e inyecta una instancia. ```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) ); }); ``` El contenedor permite un acoplamiento débil: las clases dependen de abstracciones (interfaces) en lugar de implementaciones concretas, haciendo que las pruebas y el intercambio de implementaciones sean sencillos. ### ¿Cómo Maneja Laravel las Transacciones de Base de Datos? Laravel envuelve las operaciones de base de datos en transacciones usando el método `DB::transaction()`. Las transacciones garantizan la atomicidad: o todas las operaciones tienen éxito, o todo se revierte. ```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 }); ``` Los entrevistadores frecuentemente preguntan después sobre deadlocks. La respuesta: Laravel reintenta las transacciones que fallan debido a deadlocks (configurable a través del segundo argumento de `DB::transaction()`). ### Explique los Middleware y Proporcione un Caso de Uso Real Los [Middleware](/technologies/laravel/interview-questions/middleware) filtran las solicitudes HTTP que entran en la aplicación. Cada middleware puede inspeccionar, modificar o rechazar solicitudes antes de que lleguen a los controladores. ```php // app/Http/Middleware/EnsureTeamMember.php // 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); } } ``` ```php // routes/web.php // 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']); }); ``` Los middleware se ejecutan en orden. El middleware de autenticación debe ejecutarse antes del middleware de autorización, y el middleware de logging típicamente envuelve todo. ## Resolver el Problema de Consultas N+1 El problema N+1 genera una consulta para la colección inicial más una consulta por elemento al acceder a las relaciones. Una lista de 100 artículos con autores produce 101 consultas en lugar 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(); ``` Laravel puede [prevenir el lazy loading](/technologies/laravel/interview-questions/database-migrations) en desarrollo para detectar problemas N+1 tempranamente: ```php // app/Providers/AppServiceProvider.php use Illuminate\Database\Eloquent\Model; public function boot(): void { Model::preventLazyLoading(!$this->app->isProduction()); } ``` Con esta configuración, acceder a una relación no cargada lanza una excepción en desarrollo, forzando un eager loading explícito. ## Clases Action para Operaciones de Propósito Único Las clases Action encapsulan operaciones únicas que pueden invocarse desde múltiples puntos de entrada. A diferencia de las clases de servicio que agrupan métodos relacionados, las acciones manejan una sola tarea con entradas y salidas claras. ```php // app/Actions/CreateUserAction.php // 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']); } ``` Las clases Action funcionan bien con el [sistema de jobs](/technologies/laravel/interview-questions/queues-jobs) de Laravel: la acción contiene la lógica, y el job maneja el encolamiento y el comportamiento de reintento. ## Arquitectura Basada en Eventos con Events y Listeners Los eventos desacoplan el momento en que algo sucede de las reacciones a ese evento. Cuando se realiza un pedido, el evento `OrderPlaced` se dispara. Los listeners manejan el envío de emails, la actualización de analytics y la notificación a los almacenes independientemente. ```php // app/Events/OrderPlaced.php // 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 ) {} } ``` ```php // app/Listeners/SendOrderConfirmation.php // 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) ); } } ``` ```php // app/Listeners/UpdateInventoryAnalytics.php // 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)); ``` Los listeners que implementan `ShouldQueue` se ejecutan de manera asíncrona, evitando que las operaciones lentas bloqueen la respuesta HTTP. ## Puntos Clave para Desarrolladores Laravel - Las clases de servicio extraen la lógica de negocio de los controladores, mejorando la testeabilidad y permitiendo la reutilización a través de contextos HTTP, CLI y de cola - El patrón Repository aporta valor cuando las aplicaciones necesitan caché, abstracción de fuente de datos o encapsulación de consultas complejas, pero crea sobrecarga para CRUD simple - Los filtros de Telescope reducen el ruido en producción al registrar únicamente excepciones, consultas lentas y operaciones fallidas - El Service Container gestiona la inyección de dependencias automáticamente a través de type hints, con bindings explícitos para interfaces y configuraciones complejas - Los problemas N+1 desaparecen con eager loading mediante `with()`, y `Model::preventLazyLoading()` detecta eager loads faltantes durante el desarrollo - Las clases Action manejan operaciones de propósito único invocables desde controladores, comandos, tests y jobs - Los eventos desacoplan "lo que sucedió" de "lo que debería suceder después", con listeners en cola manejando los efectos secundarios de manera asíncrona - Las respuestas en entrevistas deben demostrar comprensión de los trade-offs: cuándo los patrones ayudan versus cuándo añaden complejidad innecesaria --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/es/blog/laravel/laravel-solutions-advanced-patterns-debugging-interview