## X.7 Justificación de la Selección de Patrones de Diseño ### X.7.1 Introducción La elección de los patrones documentados en las secciones anteriores no obedeció ni al azar ni a la imitación de trabajos de grado precedentes. Es habitual que las tesis de pregrado en Tecnología e Ingeniería de Sistemas reporten un conjunto recurrente de patrones —Modelo-Vista-Controlador (MVC), Singleton, Observer, Repository y una mención superficial de Factory— cuya presencia, en la mayoría de los casos, no responde a una decisión de diseño consciente sino a una imposición del framework empleado. MVC, por ejemplo, viene dado por Laravel, Django o Spring; el Singleton de la conexión a base de datos lo provee el propio motor de persistencia; y Observer suele mencionarse en el marco teórico sin una implementación verificable en el código entregado. El resultado es un catálogo genérico que describe la herramienta utilizada, pero que no evidencia pensamiento de diseño por parte del autor. PYMETORY adopta deliberadamente el enfoque contrario. En lugar de enumerar los patrones que el framework impone, se realizó un análisis del código fuente real del sistema —apoyado en un grafo de conocimiento de 3.243 nodos y 367 comunidades— para **identificar qué problemas concretos del dominio de inventarios habían sido resueltos y mediante qué estructura**. De ese análisis emergieron cinco patrones, cada uno asociado a un problema específico y verificable: la configuración multicanal de alertas (Strategy), el control de acceso por capas (Chain of Responsibility), la unión de persistencia y dominio en las entidades (Active Record), el despacho transaccional FEFO (Transaction Script) y la construcción de datos de prueba (Factory Method). La presente sección contrasta cada uno de estos patrones con la alternativa "típica" que una tesis convencional habría reportado, y argumenta por qué dicha alternativa habría sido inadecuada o redundante para el caso particular de PYMETORY. ### X.7.2 Tabla comparativa: patrón típico vs. patrón elegido | Problema del sistema | Patrón típico en tesis | Patrón elegido en PYMETORY | Razón del cambio | |----------------------|------------------------|----------------------------|------------------| | Notificar alertas de inventario | **Observer** | **Strategy** | Los canales no son suscriptores pasivos; el administrador elige cuáles se ejecutan. El problema es *seleccionar un algoritmo*, no *avisar a una lista*. | | Controlar el acceso por rol | **Guard simple / `if` en el controlador** | **Chain of Responsibility** | Se requieren varias validaciones independientes y reordenables (auth, verified, rol), no una única comprobación monolítica. | | Acceder a los datos del inventario | **Repository** | **Active Record** | Eloquent ya *es* Active Record. Añadir un Repository sería una capa vacía —código muerto— para la complejidad real del proyecto. | | Ejecutar el despacho FEFO | **Domain Model** | **Transaction Script** | El despacho es un procedimiento lineal y transaccional de ~30 líneas. Un modelo de dominio rico sería sobreingeniería. | | Construir datos para las pruebas | **Factory (mención teórica)** | **Factory Method (uso real)** | El mismo patrón, pero como infraestructura efectiva: sin él no existirían las 60 pruebas PHPUnit. No es adorno. | ### X.7.3 Análisis de cada decisión #### a) Notificaciones: Observer → Strategy La mayoría de los trabajos que abordan un módulo de notificaciones recurren al patrón **Observer**, que modela un "sujeto" que avisa automáticamente a una lista de "observadores" suscritos cada vez que ocurre un evento. En términos sencillos, Observer es como una lista de correo: cuando algo sucede, *todos* los suscritos reciben el mismo aviso, de forma automática, sin que nadie decida caso por caso. Ese modelo no se ajusta al problema real de PYMETORY. Aquí los canales de alerta —notificación en aplicación, Telegram y correo— **no se suscriben solos ni reciben todos el mismo trato**: es el administrador quien configura explícitamente cuáles están activos, y el sistema debe *decidir en tiempo de ejecución* qué método de envío ejecutar según esa configuración. El problema, por tanto, no es "avisar a quien esté suscrito", sino "elegir cuál de varios algoritmos de envío aplicar". Esa es precisamente la definición del patrón **Strategy**: una familia de algoritmos intercambiables (cada canal) seleccionados según una condición. La evidencia está en `app/Services/AlertService.php`, método `send()` (líneas 14–25), donde cada canal se invoca condicionalmente según el valor leído de la configuración. Forzar un Observer aquí habría obligado a un mecanismo de suscripción inexistente en el dominio y habría complicado la lógica sin beneficio. #### b) Control de acceso: Guard simple → Chain of Responsibility La solución convencional al control de acceso consiste en una verificación única —un `if (Auth::check())` dentro de cada controlador, o un "guard" monolítico que comprueba todo de una sola vez. Para un profesor de sistemas, la analogía es la de un único portero en la entrada que revisa simultáneamente la identificación, el permiso y el rol del visitante. PYMETORY requiere, en cambio, **varias validaciones independientes**: que el usuario esté autenticado, que haya verificado su correo y que posea el rol adecuado. Concentrarlas en un solo bloque tendría dos defectos: mezclaría responsabilidades distintas en un mismo punto y obligaría a editar el código de cada controlador cada vez que cambie una política de seguridad. La solución adoptada es **Chain of Responsibility**: en lugar de un portero, una *fila* de porteros donde cada uno revisa una sola cosa y, si la aprueba, deja pasar al siguiente (`auth → verified → role:admin`). La evidencia está en `app/Http/Middleware/CheckRole.php`, método `handle()` (líneas 20–27), donde cada eslabón valida una condición y delega con `$next($request)`, y en su aplicación declarativa en `routes/web.php`. La ventaja práctica es decisiva: añadir, quitar o reordenar validaciones es una operación declarativa que **no toca ni un solo controlador**. #### c) Acceso a datos: Repository → Active Record El patrón **Repository** —una capa que abstrae el acceso a datos detrás de una interfaz tipo colección— es quizá el más sobreutilizado en las tesis de la región. El problema es que, sobre un ORM como Eloquent, suele ser **redundante**: Eloquent ya implementa de forma nativa el patrón **Active Record**, en el que cada modelo representa una fila de la tabla y contiene tanto su persistencia como su lógica de dominio. En lenguaje claro: el modelo `Lote` *ya sabe* leerse y guardarse en la base de datos, y además *ya sabe* calcular si está crítico o cuánto vale. Introducir un Repository encima de Eloquent, para la complejidad real de PYMETORY, habría producido una capa de envoltura que no agrega valor —"código muerto" que solo reenvía llamadas. La decisión fue **conservar Active Record** y aprovechar que el modelo `Lote` concentra las reglas del dominio: la criticidad por vencimiento (`getIsCriticalAttribute()`, líneas 57–66) y el ordenamiento FEFO (`scopeFefoOrder()`, líneas 78–81) viven en un único lugar, garantizando que el dashboard, los reportes y el módulo de consumo apliquen exactamente la misma regla. La sección X.3 documenta el contraste formal entre Active Record y su alternativa de mayor peso, Data Mapper, y por qué este último resultaría desproporcionado para el alcance del proyecto. #### d) Despacho FEFO: Domain Model → Transaction Script Una tesis que busque demostrar sofisticación podría proponer un **Domain Model**: distribuir la lógica del despacho entre múltiples objetos de dominio ricos que colaboran entre sí. Para un dominio con reglas numerosas e interdependientes, ese enfoque es el correcto. Pero el despacho FEFO de PYMETORY **no tiene esa complejidad**: es un procedimiento lineal —ordenar lotes por vencimiento, descontar cantidades, marcar los agotados y registrar cada movimiento en el Kardex— que se resuelve en unas treinta líneas dentro de una única transacción. En este escenario, un Domain Model sería **sobreingeniería**: multiplicaría clases e indirecciones sin beneficio proporcional, dificultando la lectura del código. La elección fue **Transaction Script**, que organiza la lógica como un procedimiento único por petición, con garantía de atomicidad mediante `DB::transaction`. La evidencia está en `app/Http/Controllers/ConsumptionController.php`, método `store()` (líneas 38–67). Dicho de forma sencilla: cuando el problema es una receta de pasos secuenciales con un "todo o nada", el patrón honesto es escribir esa receta de forma clara y transaccional, no fragmentarla artificialmente. #### e) Datos de prueba: Factory (teórica) → Factory Method (real) Factory es, paradójicamente, el único patrón que PYMETORY comparte con el repertorio típico. La diferencia es de fondo, no de nombre: en muchos trabajos Factory aparece como una **mención teórica** en el marco conceptual, sin una implementación que la sustente. En PYMETORY, **Factory Method es infraestructura efectiva de pruebas**: sin `LoteFactory` no existirían las 60 pruebas PHPUnit del sistema. La razón es práctica. Cada prueba necesita construir un `Lote` válido, que a su vez depende de un `Material` y una `Bodega`. Hacerlo a mano en cada prueba generaría duplicación masiva y pruebas frágiles. `LoteFactory` (método `definition()`, `database/factories/LoteFactory.php`, líneas 14–23) construye ese objeto complejo —con sus dependencias anidadas— en una sola línea de invocación. Aquí el patrón no es un adorno citado para enriquecer el marco teórico, sino el habilitador directo de la estrategia de verificación del proyecto. Esa es la distinción que separa mencionar un patrón de *usarlo con propósito*. ### X.7.4 Conclusión La selección de patrones presentada en este capítulo no constituye una lista genérica de conceptos extraídos de un catálogo, sino una **bitácora de decisiones de diseño**: para cada problema concreto del dominio de inventarios se evaluó la solución convencional, se identificaron sus limitaciones en el contexto específico de PYMETORY y se justificó la alternativa adoptada con evidencia en el código fuente. Tres de las cinco decisiones consistieron, de hecho, en **descartar deliberadamente** el patrón que una tesis típica habría incluido —Observer, Repository y Domain Model— por considerarlo inadecuado o redundante para la complejidad real del sistema. Esta capacidad de elegir y, sobre todo, de *no usar* un patrón cuando no aporta valor es la manifestación más clara del pensamiento crítico de diseño que se busca demostrar en un trabajo de grado, y diferencia a PYMETORY de los enfoques que se limitan a describir las convenciones impuestas por el framework.