Los números The Numbers
Antes de contar la historia, los datos duros. Porque en ingeniería, medir es lo primero. Before telling the story, the hard data. Because in engineering, measuring comes first.
| CapaLayer | LíneasLines | ArchivosFiles |
|---|---|---|
| Backend (API) | 83,245 | 143 |
| Frontend (Web) | 156,864 | 401 |
| App Móvil (Flutter)Mobile App (Flutter) | 43,517 | 155 |
| Scripts & Utils | 24,005 | — |
| AI Agent | 8,455 | — |
| Total | ~320,000 | 700+ |
El problema The Problem
Una empresa de transporte con +50 tractores de patio, remolques, operadores y clientes — manejando todo con hojas de Excel, mensajes de WhatsApp y llamadas telefónicas. Las asignaciones de servicio tardaban horas. Nadie sabía dónde estaba cada unidad. Los documentos vencidos se descubrían cuando ya era multa. La facturación era manual. A trucking company with 50+ yard trucks, trailers, operators and clients — managing everything with Excel sheets, WhatsApp messages and phone calls. Service assignments took hours. Nobody knew where each unit was. Expired documents were discovered when they were already fines. Invoicing was manual.
El reto: construir un sistema completo que digitalizara toda la operación — desde la solicitud de servicio hasta la factura electrónica, pasando por tracking GPS en tiempo real, gestión documental y mantenimiento preventivo. The challenge: build a complete system that digitizes the entire operation — from the service request to the electronic invoice, including real-time GPS tracking, document management and preventive maintenance.
El stack The Stack
La decisión fue clara desde el principio: TypeScript en todo. Backend, frontend y tipos compartidos. Un solo lenguaje para reducir context switching. Prisma como ORM porque las migraciones son declarativas y el type safety es real — si el modelo cambia, el compilador te lo dice antes de que el usuario lo descubra. The decision was clear from the start: TypeScript everywhere. Backend, frontend and shared types. One language to reduce context switching. Prisma as ORM because migrations are declarative and type safety is real — if the model changes, the compiler tells you before the user discovers it.
La arquitectura The Architecture
┌─────────────┐ ┌──────────────┐ ┌──────────────────┐
│ Flutter App │ │ React + MUI │ │ External APIs │
│ (Operators) │ │ (Admin/Sup) │ │ (GPS, PAC, S3) │
└──────┬──────┘ └──────┬───────┘ └────────┬─────────┘
│ │ │
└────────┬────────┘ │
▼ │
┌───────────────────────────────────────────────────────┐
│ Express.js API (Port 6060) │
│ ┌──────────┐ ┌────────────┐ ┌─────────────────────┐ │
│ │ REST API │ │ Socket.IO │ │ Background Jobs │ │
│ │ 15+ CRUD │ │ Real-time │ │ Cron / Expiration │ │
│ └──────────┘ └────────────┘ └─────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Prisma ORM → 30+ Models → MySQL 8.0 │ │
│ └──────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────┘
│ │
┌────────┘ └────────┐
▼ ▼
┌──────────┐ ┌──────────────┐
│ AWS S3 │ │ Firebase │
│ Docs │ │ Push Notif │
└──────────┘ └──────────────┘
Tres capas limpias: Routes → Controllers → Services. Prisma como singleton centralizado. WebSockets para tracking en tiempo real. Firebase para push notifications a la app móvil. S3 para documentos con URLs prefirmadas. Three clean layers: Routes → Controllers → Services. Prisma as a centralized singleton. WebSockets for real-time tracking. Firebase for push notifications to the mobile app. S3 for documents with presigned URLs.
Los módulos The Modules
No fue un MVP bonito. Fue un sistema enterprise completo. Estos son los módulos en producción: It wasn't a pretty MVP. It was a complete enterprise system. These are the production modules:
🚛 Gestión de FlotaFleet Management
Tractores, remolques, dollies. Estados, asignaciones, documentación. Integración GPS dual (Samsara + Teli).Trucks, trailers, dollies. States, assignments, documentation. Dual GPS integration (Samsara + Teli).
👷 OperadoresOperators
Perfiles, documentos (licencias, permisos, médicos), estados de disponibilidad, tracking continuo desde la app.Profiles, documents (licenses, permits, medical), availability states, continuous tracking from the app.
📋 Servicios y ViajesServices & Trips
Solicitudes de servicio, asignación de operadores y unidades, viajes con segmentos y cálculo de distancias en tiempo real.Service requests, operator and unit assignment, trips with segments and real-time distance calculation.
🧾 Facturación CFDI 4.0CFDI 4.0 Invoicing
Facturas electrónicas, Carta Porte 2.0, timbrado con PAC, PDFs, series y folios automáticos.Electronic invoices, Carta Porte 2.0, PAC stamping, PDFs, automatic series and folios.
📄 Gestión DocumentalDocument Management
Documentos de operadores, unidades y empresas en S3. Alertas de vencimiento automáticas (30, 15, 7 días).Operator, vehicle and company documents on S3. Automatic expiration alerts (30, 15, 7 days).
🔧 MantenimientoMaintenance
Preventivo y correctivo. Catálogo de piezas. Control de diesel y gastos operativos por unidad.Preventive and corrective. Parts catalog. Diesel tracking and operative expenses per unit.
💰 CotizacionesQuotations
Manuales y automatizadas con GlobalMap. Enlaces compartibles, PDFs, conversión a servicio.Manual and automated with GlobalMap. Shareable links, PDFs, conversion to service.
📊 Reportes FinancierosFinancial Reports
Estado de cuenta por cliente, reporte de facturación, control de cobranza con días de vencimiento.Account statements per client, billing reports, collections tracking with overdue days.
Las decisiones difíciles The Hard Decisions
Tracking GPS en tiempo real Real-time GPS tracking
Necesitábamos saber la ubicación de cada tractor en tiempo real. No es trivial. Hay dos proveedores GPS (Samsara y Teli), cada uno con su API distinta. Los operadores también reportan ubicación desde la app Flutter cada 30 segundos. We needed to know each truck's location in real time. It's not trivial. There are two GPS providers (Samsara and Teli), each with a different API. Operators also report location from the Flutter app every 30 seconds.
La solución: Socket.IO con múltiples namespaces. Un namespace para tracking de servicios, otro para tracking continuo de operadores. El frontend renderiza todo en un mapa en tiempo real. Cuando un supervisor abre el dashboard, ve cada unidad moviéndose — sin recargar la página. The solution: Socket.IO with multiple namespaces. One namespace for service tracking, another for continuous operator tracking. The frontend renders everything on a real-time map. When a supervisor opens the dashboard, they see each unit moving — without reloading the page.
Gestión inteligente de viajes Smart trip management
Cada solicitud de servicio se convierte en un viaje con segmentos automáticos. El sistema calcula distancias reales entre puntos (Google Distance Matrix), registra tiempos de cada etapa y permite al supervisor asignar operador y unidad considerando disponibilidad, documentos vigentes y tipo de remolque requerido. Each service request becomes a trip with automatic segments. The system calculates real distances between points (Google Distance Matrix), records timing for each stage, and lets the supervisor assign operator and unit based on availability, valid documents, and required trailer type.
El dashboard muestra en tiempo real el estado de cada viaje: pendiente, en progreso, completado. Los eventos se registran con timestamps y coordenadas GPS. Lo que antes era un caos de llamadas ahora es un flujo visual claro. The dashboard shows each trip's status in real time: pending, in progress, completed. Events are logged with timestamps and GPS coordinates. What used to be chaos of phone calls is now a clear visual workflow.
Facturación electrónica CFDI 4.0 + Carta Porte CFDI 4.0 Electronic Invoicing + Carta Porte
Si nunca has implementado facturación electrónica mexicana, imagina esto: un XML con estructura definida por el SAT, que debe firmarse digitalmente, enviarse a un PAC (Proveedor Autorizado de Certificación) para timbrado, y si es transporte necesitas el complemento Carta Porte 2.0 con datos del vehículo, operador, mercancía, origen, destino y permisos. If you've never implemented Mexican electronic invoicing, picture this: an XML with SAT-defined structure, digitally signed, sent to a PAC (Authorized Certification Provider) for stamping, and for transportation you need the Carta Porte 2.0 complement with vehicle, operator, merchandise, origin, destination and permit data.
El servicio de facturación tiene 2,400 líneas. Es el archivo más grande del proyecto. Cada campo tiene reglas del SAT que hay que cumplir al pie de la letra. Un espacio de más en un RFC y el timbrado falla. The invoicing service has 2,400 lines. It's the largest file in the project. Every field has SAT rules that must be followed to the letter. One extra space in an RFC and the stamping fails.
Lección aprendida: la facturación electrónica no es un feature — es un proyecto dentro del proyecto. Empezamos a implementarla pensando que serían 2 semanas. Fueron 6. Cada caso edge del SAT es un bug esperando aparecer. Lesson learned: electronic invoicing isn't a feature — it's a project within the project. We started thinking it would take 2 weeks. It took 6. Every SAT edge case is a bug waiting to appear.
La evolución — 7.5 meses en commits The Evolution — 7.5 Months in Commits
Lo que aprendí What I Learned
1. Prisma es brutal para productividad 1. Prisma is brutal for productivity
Con 30+ modelos interrelacionados, tener un ORM que genera tipos TypeScript automáticamente fue la diferencia entre moverme rápido y ahogarme en errores. prisma.$transaction() para operaciones atómicas. include anidados para queries complejas. El schema como fuente de verdad.
With 30+ interrelated models, having an ORM that auto-generates TypeScript types was the difference between moving fast and drowning in errors. prisma.$transaction() for atomic operations. Nested include for complex queries. The schema as the source of truth.
2. Socket.IO + React = magia para dashboards de logística 2. Socket.IO + React = magic for logistics dashboards
Ver camiones moviéndose en un mapa en tiempo real es lo que hace que el cliente diga "esto sí funciona". No es técnicamente difícil — es un useEffect que escucha eventos y actualiza el estado. Pero el impacto de negocio es enorme.
Watching trucks move on a map in real time is what makes the client say "this actually works." It's not technically hard — it's a useEffect that listens to events and updates state. But the business impact is enormous.
3. Los documentos vencidos son el problema #1 en logística 3. Expired documents are the #1 problem in logistics
Antes de este proyecto no sabía cuántos tipos de documentos necesita un tractor para operar legalmente en México. Licencia federal, verificación vehicular, seguro, tarjeta de circulación, permiso SCT... y cada uno vence. Un cron job que revisa vencimientos y dispara alertas automáticas a 30, 15 y 7 días ahorró miles de pesos en multas. Before this project I didn't know how many document types a truck needs to legally operate in Mexico. Federal license, vehicle inspection, insurance, registration, SCT permit... and each one expires. A cron job that checks expirations and fires automatic alerts at 30, 15 and 7 days saved thousands in fines.
4. AI como copiloto, no como piloto 4. AI as copilot, not pilot
Usé GitHub Copilot y Claude extensivamente durante el desarrollo. Para servicios CRUD repetitivos: increíble. Para lógica de negocio compleja como el algoritmo de asignación o la facturación: necesitas entender cada línea. La IA acelera, pero el criterio de ingeniería no es delegable. I used GitHub Copilot and Claude extensively during development. For repetitive CRUD services: incredible. For complex business logic like the assignment algorithm or invoicing: you need to understand every line. AI accelerates, but engineering judgment isn't delegable.
El dato más honesto: los tests son el punto débil. 763 líneas de tests vs 320K de código. En un proyecto real con un solo desarrollador y deadline agresivo, los tests son lo primero que se sacrifica. No estoy orgulloso, pero es la realidad. The most honest metric: tests are the weak spot. 763 lines of tests vs 320K of code. In a real project with one developer and an aggressive deadline, tests are the first thing sacrificed. I'm not proud, but it's reality.
El servidor The Server
Todo corre en una instancia EC2 m7i-flex.large (2 vCPU, 8 GB RAM). Docker Compose con 4 contenedores: API, Frontend (nginx), MySQL 8.0 y Prisma Studio. Backups automáticos a S3 con cleanup de locales cada 7 días.
Everything runs on an EC2 m7i-flex.large instance (2 vCPU, 8 GB RAM). Docker Compose with 4 containers: API, Frontend (nginx), MySQL 8.0 and Prisma Studio. Automatic backups to S3 with local cleanup every 7 days.
Con la carga actual: 3.1 GB de RAM usados, 1.1 GB de swap. Funciona, pero ya estamos cerca del límite. El siguiente paso es migrar a una instancia con 16 GB cuando entren más clientes. With current load: 3.1 GB RAM used, 1.1 GB swap. It works, but we're close to the limit. Next step is migrating to a 16 GB instance when more clients come on board.
Lo que viene What's Next
- Dashboard de métricas avanzadas — KPIs de operación, utilización de flota, rentabilidad por rutaAdvanced metrics dashboard — operational KPIs, fleet utilization, profitability per route
- Cobertura de tests — la deuda técnica más grande. Tests de integración para los endpoints críticosTest coverage — the biggest technical debt. Integration tests for critical endpoints
- Multi-tenant — adaptar el sistema para que más empresas lo usen con instancias separadasMulti-tenant — adapt the system for more companies with separate instances
- AI Agent para consultas — un agente de IA que responda preguntas sobre viajes, facturas y operación usando lenguaje naturalAI Agent for queries — an AI agent that answers questions about trips, invoices and operations using natural language