Skip to content

02. Estándar de Codificación Backend — Laravel / PHP

CampoValor
CódigoSTD-BE
Versión1.0
EstadoVigente
PropietarioTech Lead Backend / Docente
Fecha2026-08-30

Definir cómo se escribe, organiza y estructura el código PHP/Laravel del proyecto, priorizando código legible, simple y consistente sobre patrones sofisticados.

Todo el código dentro de backend/app, backend/routes, backend/database, backend/tests y configuración de Laravel.

  • PSR-1 (estilo básico) y estilo de código de Laravel — OBLIGATORIO, verificado automáticamente por Laravel Pint con el preset laravel. Este preset se basa en PSR-12 y en su sucesor mantenido, PER Coding Style (PHP-FIG marcó PSR-12 como deprecated en favor de PER-CS), con ajustes propios de Laravel. No se exige el preset psr12 estricto de Pint salvo que el equipo lo decida y lo documente.
  • PSR-4 (autoloading) — OBLIGATORIO, ya provisto por Composer/Laravel; no modificar composer.json sin justificar.
  • PHP moderno (8.1+): usar tipado estricto, readonly en DTOs, enums nativos de PHP para catálogos cerrados, match en vez de switch cuando aplique — RECOMENDADO.
  • declare(strict_types=1); al inicio de cada archivo de clase — RECOMENDADO (AVANZADO si el equipo aún no domina el tipado, pero se recomienda adoptarlo cuanto antes).
ElementoConvenciónEjemplo
ClasesPascalCase, singularInvoiceService, User
InterfacesPascalCase + sufijo InterfacePaymentGatewayInterface
TraitsPascalCase + sufijo descriptivo (able/Trait)HasUuid, LoggableTrait
Métodos y funcionescamelCase, verbo + sustantivocalculateTotal(), sendInvoice()
VariablescamelCase, nombre descriptivo, sin abreviaturas ambiguas$invoiceTotal, no $it
Constantes / enum casesSCREAMING_SNAKE_CASEMAX_ATTEMPTS, Status::ACTIVE
Archivos de claseIgual al nombre de la claseInvoiceService.php
Tablas (Eloquent)snake_case, pluralinvoices, order_items
Rutas de APIkebab-case, plural/api/v1/order-items
Variables de entornoSCREAMING_SNAKE_CASEDB_CONNECTION, MAIL_MAILER

5. Estructura de carpetas propuesta (evaluada, no sobrearquitecturada)

Section titled “5. Estructura de carpetas propuesta (evaluada, no sobrearquitecturada)”
app/
├── Http/
│ ├── Controllers/Api/V1/ # Controladores delgados, un recurso por archivo
│ ├── Requests/ # FormRequests: TODA validación de entrada vive aquí
│ ├── Resources/ # Transformación de salida (JSON API)
│ └── Middleware/
├── Models/ # Eloquent models
├── Services/ # Lógica de negocio que no pertenece al Model ni al Controller
├── Policies/ # Autorización por modelo
├── Jobs/ # Trabajos en cola (AVANZADO, cuando aplique)
├── Events/ & Listeners/ # Solo si hay un flujo realmente desacoplado (AVANZADO)
├── Exceptions/ # Excepciones de dominio propias
└── DTOs/ # Solo si se pasa >3 datos relacionados entre capas (opcional)

Justificación de cada carpeta (ninguna se incluye “porque sí”):

  • Controllers: obligatoria, punto de entrada HTTP. Deben ser delgados: reciben Request, delegan a Service o Model, devuelven Resource.
  • Requests: obligatoria desde Fase 2. Centraliza validación; evita if de validación dentro del controlador.
  • Resources: obligatoria para cualquier endpoint que devuelva datos a un frontend distinto del propio Blade (que es el caso aquí, con frontend separado).
  • Services: se usa solo cuando la lógica de negocio no cabe razonablemente en el Model ni en el Controller (p. ej. orquesta varios modelos, llama a una API externa, aplica reglas complejas). No crear un Service vacío que solo llama a Model::create().
  • Policies: obligatoria en cuanto exista más de un rol de usuario.
  • Jobs/Events/Listeners: AVANZADO. Solo si hay una necesidad real de asincronía o desacoplamiento (ej. enviar correos, generar reportes pesados). No se usan para “parecer más profesional”.
  • DTOs: opcionales. Se usan si transportar un array asociativo entre capas se vuelve confuso o propenso a errores; si el equipo aún no domina readonly class, un array tipado con FormRequest basta.

6. Repository Pattern, Factory Pattern, CQRS, Event Sourcing — cuándo NO usarlos

Section titled “6. Repository Pattern, Factory Pattern, CQRS, Event Sourcing — cuándo NO usarlos”
  • Repository Pattern: en Laravel, Eloquent implementa el patrón Active Record (el modelo encapsula acceso a datos y comportamiento). Añadir además una capa Repository propia sobre Eloquent no se implementa en este proyecto, salvo que exista una necesidad real de intercambiar la fuente de datos (por ejemplo, alternar entre PostgreSQL y una API externa) — eso se documenta con ADR. Para el 95% de los casos del proyecto, Model::query()... dentro de un Service es suficiente y más legible para el nivel del equipo.
  • Factory Pattern (de diseño, no las Model Factories de testing): no se introduce salvo que exista más de una familia real de objetos intercambiables en tiempo de ejecución (por ejemplo, más de una pasarela de pago). Las Model Factories de Laravel para testing sí son obligatorias (Doc. 08) — son un concepto distinto.
  • CQRS: fuera de alcance. Es una solución para sistemas con cargas de lectura/escritura muy asimétricas y complejas; el proyecto no tiene esa escala. No se implementa.
  • Event Sourcing: fuera de alcance total. Añade complejidad de infraestructura (event store, proyecciones) que no se justifica en un proyecto académico de un semestre.
  • Regla general: si no puedes explicar en una frase el problema concreto que el patrón resuelve en este proyecto, no lo implementes.
IDReglaPrioridadNivel
STD-BE-C01Un controlador por recurso (InvoiceController), métodos RESTful estándar (index, store, show, update, destroy).P1OBLIGATORIO
STD-BE-C02El controlador no contiene lógica de validación (usa FormRequest) ni queries complejas directas (delega a Model/Service).P1OBLIGATORIO
STD-BE-C03Cada método de controlador no supera ~25 líneas; si crece más, se extrae a un Service.P2RECOMENDADO
STD-BE-C04Los controladores siempre devuelven Resource/ResourceCollection o JsonResponse explícito, nunca Model crudo.P1OBLIGATORIO

Ejemplo correcto:

class InvoiceController extends Controller
{
public function store(StoreInvoiceRequest $request, InvoiceService $service): InvoiceResource
{
$invoice = $service->create($request->validated());
return new InvoiceResource($invoice);
}
}

Ejemplo incorrecto (validación y lógica de negocio en el controlador):

public function store(Request $request)
{
if (!$request->total || $request->total < 0) {
return response()->json(['error' => 'invalid'], 422);
}
$invoice = Invoice::create($request->all()); // Mass Assignment sin protección
return response()->json($invoice);
}
IDReglaPrioridadNivel
STD-BE-R01Toda entrada de usuario se valida con un FormRequest dedicado, nunca con $request->all() sin validar.P0OBLIGATORIO
STD-BE-R02Los mensajes de error de validación son claros y en español (idioma del proyecto).P2RECOMENDADO
STD-BE-R03La autorización básica (authorize()) del FormRequest se usa cuando aplica, en vez de comprobarlo manualmente en el controlador.P2RECOMENDADO
IDReglaPrioridadNivel
STD-BE-M01Todo modelo declara $fillable (nunca $guarded = []) para prevenir Mass Assignment.P0OBLIGATORIO
STD-BE-M02Las relaciones Eloquent se nombran según la relación real (hasMany, belongsTo, etc.), método en camelCase singular/plural según corresponda.P1OBLIGATORIO
STD-BE-M03La lógica de negocio compleja no vive en el Model; accesores/mutadores simples sí son aceptables ahí.P2RECOMENDADO
STD-BE-M04Tipar propiedades y castings ($casts) explícitamente (fechas, enums, decimales).P1OBLIGATORIO
  • Resources: transforman el modelo a la forma exacta que el frontend necesita; no exponer campos sensibles (password, tokens) — P0/OBLIGATORIO.
  • Policies: una policy por modelo con autorización condicional (ej. “solo el dueño puede editar su registro”) — P1/OBLIGATORIO en cuanto exista autenticación multiusuario.
  • Middleware: se usa para preocupaciones transversales (autenticación, rate limiting, logging de requests); no se usa para lógica de negocio — P1/OBLIGATORIO.

11. Validación, excepciones y manejo de errores

Section titled “11. Validación, excepciones y manejo de errores”
IDReglaPrioridadNivel
STD-BE-E01Las excepciones de dominio (InsufficientStockException, etc.) extienden una excepción base propia, no Exception genérica.P2RECOMENDADO
STD-BE-E02El app/Exceptions/Handler.php (o bootstrap/app.php en Laravel 11+) centraliza el mapeo de excepciones a respuestas HTTP consistentes (ver Doc. 05).P1OBLIGATORIO
STD-BE-E03Nunca se captura una excepción genérica (catch (\Exception $e)) solo para silenciarla sin loguear.P0OBLIGATORIO
STD-BE-E04Los mensajes de excepción no exponen información sensible (rutas de servidor, queries SQL) en producción.P0OBLIGATORIO

Ejemplo correcto:

try {
$service->processPayment($order);
} catch (PaymentGatewayException $e) {
Log::error('Fallo en pasarela de pago', ['order_id' => $order->id, 'error' => $e->getMessage()]);
throw new PaymentFailedException('No se pudo procesar el pago.', previous: $e);
}
  • Se usa el sistema de logging de Laravel (Log::info/warning/error), nunca dd()/var_dump()/echo en código que llega a main — P0/OBLIGATORIO.
  • Los logs de error incluyen contexto (IDs relevantes), nunca datos sensibles (contraseñas, tokens completos) — P0/OBLIGATORIO.
  • Se usa el Service Container de Laravel (type-hinting en constructores o métodos de controlador) en vez de instanciar clases con new dentro de la lógica de negocio — P2/RECOMENDADO, AVANZADO si implica bindings personalizados en AppServiceProvider.

14. SOLID, DRY, KISS, YAGNI aplicados con realismo

Section titled “14. SOLID, DRY, KISS, YAGNI aplicados con realismo”
PrincipioCómo se aplica aquí, en términos simples
Single ResponsibilityUn Controller atiende un recurso; un Service resuelve un caso de uso, no “todo lo relacionado a X”.
Open/ClosedSi algo cambia mucho (ej. reglas de descuento), extraer a una clase separada en vez de llenar de if un método existente.
LiskovSi una clase extiende otra, debe poder sustituirla sin romper el comportamiento esperado.
Interface SegregationNo forzar una interfaz con 10 métodos si una clase solo necesita 2.
Dependency InversionDepender de una interfaz (PaymentGatewayInterface) solo si realmente habrá más de una implementación; si no, se puede depender de la clase concreta sin culpa.
DRYSi copias/pegas el mismo bloque una tercera vez, extráelo a un método/trait. Copiarlo dos veces no es automáticamente una falta.
KISSLa solución más simple que resuelve el problema real gana, aunque no sea la “más elegante”.
YAGNINo construyas la opción de “por si en el futuro necesitamos multi-tenant” si nadie lo pidió.
  • Nombres que explican el “qué” sin necesitar leer el cuerpo del método.
  • Métodos con un solo nivel de abstracción (no mezclar validación de bajo nivel con orquestación de alto nivel en el mismo método).
  • Evitar banderas booleanas como parámetro (process($order, true)) — usar nombres claros o enums.
  • Máximo ~3 niveles de anidamiento (if/foreach); más que eso, extraer método.

Además de las reglas STD-BE-C0x (controllers), STD-BE-R0x (requests), STD-BE-M0x (models) y STD-BE-E0x (errores) de las secciones 7–11, este documento fija estas reglas generales, para su referencia desde la Matriz de Cumplimiento (Doc. 14):

IDReglaPrioridadSección
STD-BE-G01El código sigue el estilo de Laravel (preset laravel de Pint, base PSR-12/PER-CS); PSR-1 y PSR-4 obligatorios.P13
STD-BE-G02Se respetan las convenciones de nombres de la sección 4 (clases, métodos, variables, tablas, rutas, variables de entorno).P14
STD-BE-G03Los Resources no exponen campos sensibles (password, tokens).P010
STD-BE-G04Una Policy por modelo en cuanto exista autenticación multiusuario.P110
STD-BE-G05El middleware se usa para preocupaciones transversales, no para lógica de negocio.P110
STD-BE-G06Se usa Log::*, nunca dd()/var_dump()/echo en código que llega a main.P012
STD-BE-G07Los logs incluyen contexto y nunca datos sensibles.P012
ReglaVerificaciónHerramienta
Estilo de código (preset laravel, base PSR-12/PER-CS)AutomáticaLaravel Pint (--test en CI)
Tipado y errores estáticosAutomáticaPHPStan/Larastan nivel 4-5
Naming de clases/métodosParcial (automática + humana)PHPStan + Code Review
Estructura de capas (Controller delgado, etc.)ManualCode Review (Doc. 07)
Mass Assignment / fillableAutomática (regla PHPStan) + manualLarastan + Review
  • PSR-1, PSR-4 (php-fig.org). PSR-12 (deprecado) y su sucesor PER Coding Style (php-fig.org/per/coding-style).
  • Documentación oficial de Laravel (versión usada por el proyecto, fijada en composer.json) y de Laravel Pint.
  • Doc. 05 (API REST) para el mapeo de excepciones a códigos HTTP.
  • Doc. 08 (Pruebas) para testing de Services/Controllers.

Fin del documento 02. Continúa en 03. Estándar de Desarrollo Frontend.