Metodología de Ingeniería End-to-End

Control de Inventario & Préstamos de Equipamiento Operativo

Documentación interactiva de todo mi ciclo de trabajo: desde la auditoría inicial de ineficiencias críticas y modelado de datos en 3FN, hasta el tablero ágil, arquitectura de microservicios con transacciones ACID y entrega en producción.

Problema Detectado

Descoordinación en planillas Excel, demoras de 45 min por relevo de guardia y 0% de trazabilidad en pérdidas materiales.

Solución Planteada

Plataforma transaccional centralizada con bloqueo atómico de concurrencia y cadena de custodia en tiempo real.

Stack Tecnológico

Node.js / TypeScript, PostgreSQL (ACID), Tailwind CSS, Vanilla JS reactivo y suite de pruebas unitarias Jest.

Metodología Aplicada

Kanban iterativo con límites WIP, Domain-Driven Design (DDD lite) y especificaciones ejecutables BDD en Gherkin.

Fase 01 · Descubrimiento y Análisis

Definición del Problema & Especificación BDD

Antes de escribir una sola línea de código, se realizó una auditoría minuciosa del flujo de trabajo existente para identificar cuellos de botella operativos, riesgos de duplicidad y requisitos de negocio prioritarios.

Antes: Proceso Analógico / Excel

Crítico / Ineficiente
  • Doble asignación accidental: Dos guardias prestaban simultáneamente el mismo equipo sin enterarse hasta el fin de turno.
  • 45 minutos de demora por relevo: Búsqueda manual en carpetas físicas y cuadernos de guardia para conciliar firmas.
  • Equipos dañados no segregados: Equipamiento con fallas reportadas verbalmente continuaba figurando como apto para servicio.
  • Cero trazabilidad forense: Imposibilidad de determinar quién fue el último custodio ante daños por mal uso.

Después: Plataforma Centralizada Transaccional

Optimizado / Automatizado
  • Validación atómica de concurrencia: Bloqueo pesimista en base de datos. 0% de probabilidad de préstamos dobles.
  • Relevo en menos de 3 minutos: Escaneo rápido de código de inventario y confirmación digital instantánea.
  • Pase automático a mantenimiento: El reporte de una novedad bloquea automáticamente el equipo impidiendo su reasignación.
  • Auditoría digital inmutable: Historial cronológico con ID de operador, agente receptor, timestamp y geolocalización.

Historias de Usuario Clave con Criterios de Aceptación Gherkin

Como operador de sala de armas / pañol, quiero registrar la entrega de un equipo a un agente validando su aptitud en tiempo real, para garantizar que solo se entreguen recursos habilitados y mantener la cadena de custodia intacta.

Escenario: Asignación exitosa de equipo disponible sin sanciones activas Dado que el equipo con código "RAD-MOT-04" se encuentra en estado "DISPONIBLE" Y el agente con legajo "AG-8841" no registra préstamos vencidos ni bloqueos Cuando el operador confirma la solicitud de préstamo para el turno actual Entonces el sistema inicia una transacción atómica Y actualiza el estado del equipo a "PRESTADO" Y crea un registro en la tabla "prestamos" con timestamp del servidor Y emite el comprobante de entrega digital con código de verificación.

Como arquitecto de software, quiero garantizar que dos terminales simultáneas no puedan adjudicar el mismo ítem en paralelo, para asegurar la consistencia estricta del inventario físico.

Escenario: Intento de préstamo simultáneo de un mismo equipo Dado que dos operadores intentan prestar el equipo "VIS-NOC-02" en el mismo milisegundo Cuando ambas peticiones ingresan al servicio transaccional Entonces la primera transacción adquiere el bloqueo pesimista FOR UPDATE Y la segunda transacción se bloquea hasta que el estado muta a "PRESTADO" Y la segunda petición es rechazada con código HTTP 409 y mensaje "EquipmentAlreadyLoanedException" Y no se produce inconsistencia ni duplicación en la base de datos.

Como responsable de inspección técnica, quiero marcar una unidad como defectuosa al recibirla de un servicio, para evitar que vuelva a ser despachada al personal operativo sin antes ser revisada.

Escenario: Devolución con novedad técnica reportada Dado que el agente devuelve el equipo "DET-GAR-01" con falla de encendido Cuando el operador selecciona la opción "Devolver con Falla Técnica" Entonces el préstamo se cierra como "FINALIZADO_CON_NOVEDAD" Y el equipo pasa automáticamente al estado "TALLER" Y se crea una orden de servicio en la tabla "mantenimiento" con alerta al taller.
Fase 02 · Organización y Ejecución

Gestión Ágil & Tablero Kanban Interactivo

Estructuración del backlog en sprints cortos con límites de trabajo en curso (WIP Limits). Prueba arrastrar las tarjetas entre columnas o filtrarlas por disciplina técnica para observar la distribución del esfuerzo.

Filtrar por Capa:
Arrastra tarjetas o usa las flechas [←] [→] para avanzar
Backlog Priorizado 3
Backend Media
Módulo de exportación de auditorías a formato CSV / Excel firmado
Frontend Baja
Modo de alto contraste para terminales de guardia nocturna
Database Media
Estrategia de particionado de tabla histórica de préstamos anuales
En Progreso (WIP: 3) 2
Backend Crítica
Servicio transaccional con SELECT FOR UPDATE y rollback automático
Frontend Alta
Consola reactiva de búsqueda y despacho de equipos con teclado rápido
En Revisión / QA 2
Testing Crítica
Test de concurrencia: simulación de 50 peticiones simultáneas sobre 1 ítem
Database Alta
Índices parciales únicos para control de préstamos activos en PostgreSQL
Completado (Done) 4
Database Aprobado
Modelado DER normalizado en 3FN y migraciones versionadas
Backend Aprobado
Arquitectura en capas: Controllers, Services y Repositories desacoplados
Frontend Aprobado
Diseño UI/UX accesible (a11y) con contrastes validados WCAG AA
Testing Aprobado
Configuración de pipeline CI/CD con ejecución automatizada de tests Jest
Fase 03 · Modelado y Resiliencia

Arquitectura de Datos & Diagrama Entidad-Relación

Diseño relacional en Tercera Forma Normal (3FN). Haz clic o pasa el cursor sobre las entidades para inspeccionar sus claves foráneas, índices de integridad y relaciones 1:N.

Diagrama Relacional (DER) Normalizado

Interactúa con una tabla para ver sus dependencias
📦 equipos Master
PK id UUID
codigo_inventario VARCHAR(30) UNIQUE
modelo VARCHAR(100)
categoria ENUM
estado ENUM(disp, prest, tall)
created_at TIMESTAMPTZ
📋 prestamos Transaccional
PK id UUID
FK equipo_id UUID → equipos.id
FK usuario_id UUID → usuarios.id
fecha_prestamo TIMESTAMPTZ
fecha_devolucion TIMESTAMPTZ NULL
estado ENUM(activo, devuelto)
👤 usuarios Custodios
PK id UUID
legajo VARCHAR(20) UNIQUE
nombre_completo VARCHAR(120)
departamento VARCHAR(60)
activo BOOLEAN DEFAULT true
🛠 mantenimiento Servicio Técnico
PK id UUID
FK equipo_id UUID → equipos.id
tipo_falla TEXT
fecha_ingreso TIMESTAMPTZ
fecha_egreso TIMESTAMPTZ NULL
diagnostico TEXT NULL

Aislamiento & Bloqueo Pesimista

Se utiliza SELECT ... FOR UPDATE durante la creación del préstamo. Esto garantiza a nivel de motor SQL que ninguna otra transacción concurrentemente intente operar sobre el mismo equipo, eliminando condiciones de carrera.

Índice Único Parcial en Base de Datos

Se implementó una restricción física: CREATE UNIQUE INDEX idx_equipo_activo ON prestamos (equipo_id) WHERE estado = 'ACTIVO';. Aunque la capa de aplicación fallara, la base de datos rechaza cualquier duplicidad por constraint.

Trazabilidad Histórica Inmutable

La tabla prestamos opera bajo principio de solo adición (append-only) para auditorías forenses. Los datos de fecha, custodio y recepción se sellan de manera inalterable para peritajes legales.

Fase 04 · Implementación Técnica

Ingeniería de Software, Clean Code & Tests

Código tipado, separación estricta de responsabilidades (Service / Repository pattern) y pruebas automatizadas que verifican los casos de borde críticos antes de llegar a producción.

import { PoolClient } from 'pg';
import { EquipmentAlreadyLoanedException, UserBlockedException } from '../exceptions';

export class LoanService {
  constructor(private readonly dbPool: any) {}

  /**
   * Registra un préstamo asegurando atomicidad y prevención de race conditions.
   */
  async registrarPrestamo(equipoId: string, usuarioId: string, operadorId: string): Promise<PrestamoResult> {
    const client: PoolClient = await this.dbPool.connect();

    try {
      await client.query('BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED');

      // 1. Bloqueo pesimista sobre la fila del equipo: previene carreras concurrentes
      const equipRes = await client.query(
        'SELECT id, estado, modelo FROM equipos WHERE id = $1 FOR UPDATE',
        [equipoId]
      );

      if (equipRes.rows.length === 0) {
        throw new Error('El equipo especificado no existe en inventario');
      }

      const equipo = equipRes.rows[0];
      if (equipo.estado !== 'DISPONIBLE') {
        throw new EquipmentAlreadyLoanedException(
          `El equipo ${equipo.modelo} no está disponible (Estado actual: ${equipo.estado})`
        );
      }

      // 2. Validar que el usuario solicitante esté activo
      const userRes = await client.query(
        'SELECT id, activo FROM usuarios WHERE id = $1',
        [usuarioId]
      );
      if (!userRes.rows[0]?.activo) {
        throw new UserBlockedException('El agente solicitante se encuentra inhabilitado');
      }

      // 3. Insertar el préstamo y mutar el estado del equipo de forma atómica
      const insertLoanQuery = `
        INSERT INTO prestamos (equipo_id, usuario_id, operador_id, estado, fecha_prestamo)
        VALUES ($1, $2, $3, 'ACTIVO', NOW())
        RETURNING id, fecha_prestamo;
      `;
      const loanRes = await client.query(insertLoanQuery, [equipoId, usuarioId, operadorId]);

      await client.query(
        'UPDATE equipos SET estado = \'PRESTADO\' WHERE id = $1',
        [equipoId]
      );

      await client.query('COMMIT');

      return {
        prestamoId: loanRes.rows[0].id,
        fechaPrestamo: loanRes.rows[0].fecha_prestamo,
        exitoso: true
      };
    } catch (error) {
      await client.query('ROLLBACK');
      throw error;
    } finally {
      client.release();
    }
  }
}
Fase 05 · Puesta en Producción

Producto Final: Consola Operativa en Vivo

Simulador interactivo del panel de guardia. Puedes buscar equipos, filtrar por estado y accionar préstamos o devoluciones en tiempo real para verificar la reactividad del estado y la bitácora de eventos.

Consola de Guardia · Control Operativo en Tiempo Real

6
Total Equipamiento
3
Disponibles en Pañol
2
En Custodia / Guardia
1
En Taller / Mantenimiento
Código Modelo & Categoría Estado Actual Custodio Asignado Acción Operativa
Bitácora de Auditoría en Tiempo Real
[08:00:12] Sistema iniciado. 6 unidades sincronizadas con la base de datos principal.

¿Interesado en implementar este nivel de arquitectura en su organización?

Combino metodologías ágiles rigurosas, diseño de bases de datos resilientes y desarrollo de software limpio para transformar procesos críticos en ventajas competitivas estables.